From c4b79a2fbfb28308e958e4ffdd988f3cf678fe2a Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 7 May 2025 10:01:38 +0200 Subject: [PATCH 001/868] ASoC: qcom: sm8250: set card driver name from match data Sound machine drivers for Qualcomm SoCs can be reused across multiple SoCs. But user space ALSA UCM files depend on the card driver name which should be set per board/SoC. Allow such customization by using driver match data as sound card driver name. The QRB4210 RB2 gets its name set to "sm4250" as requested by Srinivas Kandagatla, and since no (known) UCM has been written yet this should not break anything. Also while we're already touching these lines, sort the compatibles alphabetically. Reviewed-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Signed-off-by: Luca Weiss Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250507-fp5-dp-sound-v4-2-4098e918a29e@fairphone.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index b70b2a5031dfb..f0d83a843765d 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -16,7 +16,6 @@ #include "usb_offload_utils.h" #include "sdw.h" -#define DRIVER_NAME "sm8250" #define MI2S_BCLK_RATE 1536000 struct sm8250_snd_data { @@ -200,15 +199,15 @@ static int sm8250_platform_probe(struct platform_device *pdev) if (ret) return ret; - card->driver_name = DRIVER_NAME; + card->driver_name = of_device_get_match_data(dev); sm8250_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } static const struct of_device_id snd_sm8250_dt_match[] = { - {.compatible = "qcom,sm8250-sndcard"}, - {.compatible = "qcom,qrb4210-rb2-sndcard"}, - {.compatible = "qcom,qrb5165-rb5-sndcard"}, + { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, + { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, + { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, {} }; -- GitLab From ed82808c6a0f333e51fee4e97cbe8e0189b7f354 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 7 May 2025 10:01:39 +0200 Subject: [PATCH 002/868] ASoC: qcom: sm8250: add DisplayPort Jack support Add support for DisplayPort Jack events, so that user space can configure the audio routing correctly. Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250507-fp5-dp-sound-v4-3-4098e918a29e@fairphone.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index f0d83a843765d..2317fe285ee7d 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -25,6 +25,7 @@ struct sm8250_snd_data { struct snd_soc_jack jack; struct snd_soc_jack usb_offload_jack; bool usb_offload_jack_setup; + struct snd_soc_jack dp_jack; bool jack_setup; }; @@ -32,14 +33,16 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); - int ret; - if (cpu_dai->id == USB_RX) - ret = qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, - &data->usb_offload_jack_setup); - else - ret = qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); - return ret; + switch (cpu_dai->id) { + case DISPLAY_PORT_RX: + return qcom_snd_dp_jack_setup(rtd, &data->dp_jack, 0); + case USB_RX: + return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack, + &data->usb_offload_jack_setup); + default: + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + } } static void sm8250_snd_exit(struct snd_soc_pcm_runtime *rtd) -- GitLab From e6e8897995a9e6028563ce36c27877e5478c8571 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 7 May 2025 10:01:40 +0200 Subject: [PATCH 003/868] ASoC: qcom: sm8250: Add Fairphone 5 soundcard compatible Add a compatible for the QCM6490-based Fairphone 5 which can use this machine driver. As a note, QCM6490 RB3 board is using audioreach architecture while Fairphone 5 uses pre-audioreach. Reviewed-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250507-fp5-dp-sound-v4-4-4098e918a29e@fairphone.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 2317fe285ee7d..69c514fad0b1b 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -208,6 +208,7 @@ static int sm8250_platform_probe(struct platform_device *pdev) } static const struct of_device_id snd_sm8250_dt_match[] = { + { .compatible = "fairphone,fp5-sndcard", .data = "qcm6490" }, { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" }, -- GitLab From 0045b902ad27f2676c2a5b6444494a8287b80072 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:59:55 +0200 Subject: [PATCH 004/868] ASoC: codecs: Constify regmap configuration static variables Static arrays/structs for regmap configuration like 'struct reg_default', 'struct reg_sequence' and others are not modified so can be changed to const for more safety. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Charles Keepax Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-1-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l36.c | 2 +- sound/soc/codecs/da7218.c | 2 +- sound/soc/codecs/da7219.c | 4 ++-- sound/soc/codecs/es8375.c | 2 +- sound/soc/codecs/max98363.c | 2 +- sound/soc/codecs/max98373-i2c.c | 2 +- sound/soc/codecs/max98373-sdw.c | 2 +- sound/soc/codecs/max98388.c | 2 +- sound/soc/codecs/max98390.c | 2 +- sound/soc/codecs/max98396.c | 4 ++-- sound/soc/codecs/max98504.c | 2 +- sound/soc/codecs/max98520.c | 2 +- sound/soc/codecs/max98927.c | 2 +- sound/soc/codecs/rt722-sdca-sdw.c | 2 +- sound/soc/codecs/wcd938x.c | 2 +- sound/soc/codecs/wsa881x.c | 4 ++-- sound/soc/codecs/wsa883x.c | 2 +- sound/soc/codecs/wsa884x.c | 2 +- 18 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index b49c6905e8727..b60697ff7a506 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -129,7 +129,7 @@ static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { {27000000, 0x3F, 0x0A}, }; -static struct reg_default cs35l36_reg[] = { +static const struct reg_default cs35l36_reg[] = { {CS35L36_TESTKEY_CTRL, 0x00000000}, {CS35L36_USERKEY_CTL, 0x00000000}, {CS35L36_OTP_CTRL1, 0x00002460}, diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 5f2f67e3bd292..a7539e1a18939 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3033,7 +3033,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7218 = { * Regmap configs */ -static struct reg_default da7218_reg_defaults[] = { +static const struct reg_default da7218_reg_defaults[] = { { DA7218_SYSTEM_ACTIVE, 0x00 }, { DA7218_CIF_CTRL, 0x00 }, { DA7218_SPARE1, 0x00 }, diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 3958e88a24456..221577574525a 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2312,7 +2312,7 @@ static void da7219_handle_pdata(struct snd_soc_component *component) * Regmap configs */ -static struct reg_default da7219_reg_defaults[] = { +static const struct reg_default da7219_reg_defaults[] = { { DA7219_MIC_1_SELECT, 0x00 }, { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, { DA7219_SR_24_48, 0x00 }, @@ -2443,7 +2443,7 @@ static const struct regmap_config da7219_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static struct reg_sequence da7219_rev_aa_patch[] = { +static const struct reg_sequence da7219_rev_aa_patch[] = { { DA7219_REFERENCES, 0x08 }, }; diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index 0092596321075..36b0ebdce514d 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -620,7 +620,7 @@ static bool es8375_writeable_register(struct device *dev, unsigned int reg) } } -static struct regmap_config es8375_regmap_config = { +static const struct regmap_config es8375_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = ES8375_REG_MAX, diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 950105e5bffdc..fd6830a7579d4 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -14,7 +14,7 @@ #include "max98363.h" -static struct reg_default max98363_reg[] = { +static const struct reg_default max98363_reg[] = { {MAX98363_R2021_ERR_MON_CTRL, 0x0}, {MAX98363_R2022_SPK_MON_THRESH, 0x0}, {MAX98363_R2023_SPK_MON_DURATION, 0x0}, diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index 56c4ba1f37826..f58b8c8625a70 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -23,7 +23,7 @@ static const u32 max98373_i2c_cache_reg[] = { MAX98373_R20B6_BDE_CUR_STATE_READBACK, }; -static struct reg_default max98373_reg[] = { +static const struct reg_default max98373_reg[] = { {MAX98373_R2000_SW_RESET, 0x00}, {MAX98373_R2001_INT_RAW1, 0x00}, {MAX98373_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 6088278e6503d..43b52bda6ad52 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -26,7 +26,7 @@ static const u32 max98373_sdw_cache_reg[] = { MAX98373_R20B6_BDE_CUR_STATE_READBACK, }; -static struct reg_default max98373_reg[] = { +static const struct reg_default max98373_reg[] = { {MAX98373_R0040_SCP_INIT_STAT_1, 0x00}, {MAX98373_R0041_SCP_INIT_MASK_1, 0x00}, {MAX98373_R0042_SCP_INIT_STAT_2, 0x00}, diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c index 99986090b4a63..076f15a9867e1 100644 --- a/sound/soc/codecs/max98388.c +++ b/sound/soc/codecs/max98388.c @@ -18,7 +18,7 @@ #include #include "max98388.h" -static struct reg_default max98388_reg[] = { +static const struct reg_default max98388_reg[] = { {MAX98388_R2000_SW_RESET, 0x00}, {MAX98388_R2001_INT_RAW1, 0x00}, {MAX98388_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 76296176f9486..a8a282ff9fc5a 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -23,7 +23,7 @@ #include "max98390.h" -static struct reg_default max98390_reg_defaults[] = { +static const struct reg_default max98390_reg_defaults[] = { {MAX98390_INT_EN1, 0xf0}, {MAX98390_INT_EN2, 0x00}, {MAX98390_INT_EN3, 0x00}, diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index c1888cd83dbc6..4b4e1fc98a6da 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -16,7 +16,7 @@ static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = { "dvddio", }; -static struct reg_default max98396_reg[] = { +static const struct reg_default max98396_reg[] = { {MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00}, {MAX98396_R2002_INT_RAW2, 0x00}, @@ -174,7 +174,7 @@ static struct reg_default max98396_reg[] = { {MAX98396_R21FF_REVISION_ID, 0x00}, }; -static struct reg_default max98397_reg[] = { +static const struct reg_default max98397_reg[] = { {MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00}, {MAX98396_R2002_INT_RAW2, 0x00}, diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 6b6a7ece4cecc..c94142768c818 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -35,7 +35,7 @@ struct max98504_priv { unsigned int brownout_release_rate; }; -static struct reg_default max98504_reg_defaults[] = { +static const struct reg_default max98504_reg_defaults[] = { { 0x01, 0}, { 0x02, 0}, { 0x03, 0}, diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index adf5a898c6df5..2bf8976c18282 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -16,7 +16,7 @@ #include #include "max98520.h" -static struct reg_default max98520_reg[] = { +static const struct reg_default max98520_reg[] = { {MAX98520_R2000_SW_RESET, 0x00}, {MAX98520_R2001_STATUS_1, 0x00}, {MAX98520_R2002_STATUS_2, 0x00}, diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 55cc18451a2d2..0e9b8970997cf 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -19,7 +19,7 @@ #include #include "max98927.h" -static struct reg_default max98927_reg[] = { +static const struct reg_default max98927_reg[] = { {MAX98927_R0001_INT_RAW1, 0x00}, {MAX98927_R0002_INT_RAW2, 0x00}, {MAX98927_R0003_INT_RAW3, 0x00}, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 609ca0d6c83a1..70700bdb80a14 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -147,7 +147,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) } } -static struct regmap_sdw_mbq_cfg rt722_mbq_config = { +static const struct regmap_sdw_mbq_cfg rt722_mbq_config = { .mbq_size = rt722_sdca_mbq_size, }; diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index d9b61eab029af..342d1f7d5dee3 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -275,7 +275,7 @@ static const struct regmap_irq wcd938x_irqs[WCD938X_NUM_IRQS] = { REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), }; -static struct regmap_irq_chip wcd938x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd938x_regmap_irq_chip = { .name = "wcd938x", .irqs = wcd938x_irqs, .num_irqs = ARRAY_SIZE(wcd938x_irqs), diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 6627d2da37220..d479521a6d504 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -202,7 +202,7 @@ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ snd_soc_get_volsw, wsa881x_put_pa_gain, tlv_array) -static struct reg_default wsa881x_defaults[] = { +static const struct reg_default wsa881x_defaults[] = { { WSA881X_CHIP_ID0, 0x00 }, { WSA881X_CHIP_ID1, 0x00 }, { WSA881X_CHIP_ID2, 0x00 }, @@ -346,7 +346,7 @@ static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { }; /* Default register reset values for WSA881x rev 2.0 */ -static struct reg_sequence wsa881x_rev_2_0[] = { +static const struct reg_sequence wsa881x_rev_2_0[] = { { WSA881X_RESET_CTL, 0x00, 0x00 }, { WSA881X_TADC_VALUE_CTL, 0x01, 0x00 }, { WSA881X_INTR_MASK, 0x1B, 0x00 }, diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index f04d99c66f332..13c9d4a6f0153 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -572,7 +572,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { }, }; -static struct reg_default wsa883x_defaults[] = { +static const struct reg_default wsa883x_defaults[] = { { WSA883X_REF_CTRL, 0xD5 }, { WSA883X_TEST_CTL_0, 0x06 }, { WSA883X_BIAS_0, 0xD2 }, diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index fd6ebc25fe894..07d8a2645404e 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -899,7 +899,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { }, }; -static struct reg_default wsa884x_defaults[] = { +static const struct reg_default wsa884x_defaults[] = { { WSA884X_BG_CTRL, 0xa5 }, { WSA884X_ADC_CTRL, 0x00 }, { WSA884X_BOP1_PROG, 0x22 }, -- GitLab From 239dab898b739f49b1bda8d65163fe4f5c773468 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:59:56 +0200 Subject: [PATCH 005/868] ASoC: fsl: Constify reg_default array Static 'struct reg_default' array is not modified so can be changed to const for more safety. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-2-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- sound/soc/fsl/fsl_sai.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 677529916dc0e..637260f306eca 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -930,7 +930,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) } } -static struct reg_default fsl_asrc_reg[] = { +static const struct reg_default fsl_asrc_reg[] = { { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 }, { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 }, { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 }, diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index af1a168d35e37..c84e3bb1290fd 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1059,7 +1059,7 @@ static const struct snd_soc_component_driver fsl_component = { .legacy_dai_naming = 1, }; -static struct reg_default fsl_sai_reg_defaults_ofs0[] = { +static const struct reg_default fsl_sai_reg_defaults_ofs0[] = { {FSL_SAI_TCR1(0), 0}, {FSL_SAI_TCR2(0), 0}, {FSL_SAI_TCR3(0), 0}, @@ -1082,7 +1082,7 @@ static struct reg_default fsl_sai_reg_defaults_ofs0[] = { {FSL_SAI_RMR, 0}, }; -static struct reg_default fsl_sai_reg_defaults_ofs8[] = { +static const struct reg_default fsl_sai_reg_defaults_ofs8[] = { {FSL_SAI_TCR1(8), 0}, {FSL_SAI_TCR2(8), 0}, {FSL_SAI_TCR3(8), 0}, -- GitLab From 620d9687004ce877b340041b8213da166f329367 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:59:57 +0200 Subject: [PATCH 006/868] ASoC: codecs: wcd9335: Drop unused sido_input_src field Member wcd9335_codec.sido_input_src is not read anywhere after assignment, so it can be safely dropped. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-3-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 5e19e813748df..1c050b8c19de4 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -312,7 +312,6 @@ struct wcd9335_codec { u32 num_rx_port; u32 num_tx_port; - int sido_input_src; enum wcd9335_sido_voltage sido_voltage; struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; @@ -4725,8 +4724,6 @@ static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = { static void wcd9335_enable_sido_buck(struct snd_soc_component *component) { - struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); - snd_soc_component_update_bits(component, WCD9335_ANA_RCO, WCD9335_ANA_RCO_BG_EN_MASK, WCD9335_ANA_RCO_BG_ENABLE); @@ -4740,7 +4737,6 @@ static void wcd9335_enable_sido_buck(struct snd_soc_component *component) WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT); /* 100us sleep needed after VREF settings */ usleep_range(100, 110); - wcd->sido_input_src = SIDO_SOURCE_RCO_BG; } static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp) @@ -4871,7 +4867,6 @@ static int wcd9335_probe(struct wcd9335_codec *wcd) memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs)); memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs)); - wcd->sido_input_src = SIDO_SOURCE_INTERNAL; wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; return devm_snd_soc_register_component(dev, &wcd9335_component_drv, -- GitLab From 9afc53569d800f5e3caf9401d49ad2d89f340a54 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:59:58 +0200 Subject: [PATCH 007/868] ASoC: codecs: wcd934x: Drop unused num_rx_port/num_tx_port fields Members wcd934x_codec.num_rx_port and num_tx_port are not read anywhere after assignment, so they can be safely dropped. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-4-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index fa69817c97eaf..1bb7e1dc7e6b0 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -537,8 +537,6 @@ struct wcd934x_codec { int rate; u32 version; u32 hph_mode; - int num_rx_port; - int num_tx_port; u32 tx_port_value[WCD934X_TX_MAX]; u32 rx_port_value[WCD934X_RX_MAX]; int sido_input_src; @@ -1928,13 +1926,11 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai, return -EINVAL; } - wcd->num_rx_port = rx_num; for (i = 0; i < rx_num; i++) { wcd->rx_chs[i].ch_num = rx_slot[i]; INIT_LIST_HEAD(&wcd->rx_chs[i].list); } - wcd->num_tx_port = tx_num; for (i = 0; i < tx_num; i++) { wcd->tx_chs[i].ch_num = tx_slot[i]; INIT_LIST_HEAD(&wcd->tx_chs[i].list); -- GitLab From 87a2270fd1f560dbfc1b26391ff3b37f56d2a1a3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:59:59 +0200 Subject: [PATCH 008/868] ASoC: codecs: wcd937x: Drop unused 'struct wcd937x_priv' fields 'wcd_regmap_irq_chip' and 'jack' in 'struct wcd937x_priv' are not used at all. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-5-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index b9df58b86ce95..126a29e0b3ff9 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -88,10 +88,8 @@ struct wcd937x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; struct regulator_bulk_data supplies[WCD937X_MAX_BULK_SUPPLY]; - struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD937X_MAX_MICBIAS]; s32 pullup_ref[WCD937X_MAX_MICBIAS]; -- GitLab From fd32bd4467c13254cb52188034fce242f0f6340d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 22:00:00 +0200 Subject: [PATCH 009/868] ASoC: codecs: wcd938x: Drop unused 'struct wcd938x_priv' fields 'wcd_regmap_irq_chip' and 'jack' in 'struct wcd938x_priv' are not used at all. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-6-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 342d1f7d5dee3..43347c14070ca 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -159,10 +159,8 @@ struct wcd938x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; struct regulator_bulk_data supplies[WCD938X_MAX_SUPPLY]; - struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD938X_MAX_MICBIAS]; s32 pullup_ref[WCD938X_MAX_MICBIAS]; -- GitLab From ff228b6b9ed8f5d7ef418b6cbece772c6617d789 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 22:00:01 +0200 Subject: [PATCH 010/868] ASoC: codecs: wcd938x: Drop unused variant field Member wcd938x_priv.variant is assigned in probe() function and used immediately thereafter, thus it can be just a local variable for less variables stored in 'struct wcd938x_priv' device-wide state. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-7-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 43347c14070ca..5a751056a98a5 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -168,7 +168,6 @@ struct wcd938x_priv { u32 tx_mode[TX_ADC_MAX]; int flyback_cur_det_disable; int ear_rx_path; - int variant; struct gpio_desc *reset_gpio; struct gpio_desc *us_euro_gpio; struct mux_control *us_euro_mux; @@ -3044,6 +3043,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev; struct device *dev = component->dev; unsigned long time_left; + unsigned int variant; int ret, i; time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, @@ -3059,9 +3059,9 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; - wcd938x->variant = snd_soc_component_read_field(component, - WCD938X_DIGITAL_EFUSE_REG_0, - WCD938X_ID_MASK); + variant = snd_soc_component_read_field(component, + WCD938X_DIGITAL_EFUSE_REG_0, + WCD938X_ID_MASK); wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X); if (IS_ERR(wcd938x->clsh_info)) { @@ -3115,14 +3115,14 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd938x->hphl_pdm_wd_int); disable_irq_nosync(wcd938x->aux_pdm_wd_int); - switch (wcd938x->variant) { + switch (variant) { case WCD9380: ret = snd_soc_add_component_controls(component, wcd9380_snd_controls, ARRAY_SIZE(wcd9380_snd_controls)); if (ret < 0) { dev_err(component->dev, "%s: Failed to add snd ctrls for variant: %d\n", - __func__, wcd938x->variant); + __func__, variant); goto err_free_aux_pdm_wd_int; } break; @@ -3132,7 +3132,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) if (ret < 0) { dev_err(component->dev, "%s: Failed to add snd ctrls for variant: %d\n", - __func__, wcd938x->variant); + __func__, variant); goto err_free_aux_pdm_wd_int; } break; -- GitLab From ece5d881004f041c2e1493436409dbcbea3ad5f8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 22:00:02 +0200 Subject: [PATCH 011/868] ASoC: codecs: wcd939x: Drop unused 'struct wcd939x_priv' fields 'wcd_regmap_irq_chip' and 'jack' in 'struct wcd939x_priv' are not used at all. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Srinivas Kandagatla Link: https://patch.msgid.link/20250528-asoc-const-unused-v1-8-19a5d07b9d5c@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 067d23c7ecf90..bfd4d2c8bdc9b 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -190,10 +190,8 @@ struct wcd939x_priv { struct wcd_mbhc_intr intr_ids; struct wcd_clsh_ctrl *clsh_info; struct irq_domain *virq; - struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY]; - struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD939X_MAX_MICBIAS]; s32 pullup_ref[WCD939X_MAX_MICBIAS]; -- GitLab From 077caf1d1763e069239101b4a72351fb38d37f13 Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 3 Jun 2025 13:51:07 +0800 Subject: [PATCH 012/868] ASoC: renesas: Use helper function for_each_child_of_node_scoped() The for_each_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Acked-by: Kuninori Morimoto Signed-off-by: Ai Chao Link: https://patch.msgid.link/20250603055109.3154061-3-aichao@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/core.c | 35 +++++++++++------------------------ sound/soc/renesas/rcar/ctu.c | 8 ++------ sound/soc/renesas/rcar/dma.c | 4 +--- sound/soc/renesas/rcar/dvc.c | 8 ++------ sound/soc/renesas/rcar/mix.c | 8 ++------ sound/soc/renesas/rcar/src.c | 10 ++-------- sound/soc/renesas/rcar/ssi.c | 18 +++++------------- sound/soc/renesas/rcar/ssiu.c | 7 ++----- 8 files changed, 27 insertions(+), 71 deletions(-) diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index a72f36d3ca2cd..37d954495ea5a 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -1075,7 +1075,6 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); - struct device_node *np; int is_play = rsnd_io_is_play(io); int i; @@ -1094,7 +1093,7 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, if (!node) break; - for_each_child_of_node(ssiu_np, np) { + for_each_child_of_node_scoped(ssiu_np, np) { if (np == node) { rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); dev_dbg(dev, "%s is part of TDM Split\n", io->name); @@ -1154,21 +1153,18 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, { struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; int i; if (!node) return; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = mod_get(priv, i); @@ -1217,16 +1213,13 @@ int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *na int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name) { struct device *dev = rsnd_priv_to_dev(priv); - struct device_node *np; int i; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { i = rsnd_node_fixed_index(dev, np, name, i); - if (i < 0) { - of_node_put(np); + if (i < 0) return 0; - } i++; } @@ -1250,7 +1243,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; - struct device_node *ports, *node; + struct device_node *node; int nr = 0; int i = 0; @@ -1270,7 +1263,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) of_node_put(node); - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (!of_node_name_eq(node, RSND_NODE_DAI)) continue; @@ -1279,7 +1272,6 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) i++; if (i >= RSND_MAX_COMPONENT) { dev_info(dev, "reach to max component\n"); - of_node_put(node); break; } } @@ -1290,7 +1282,7 @@ audio_graph: /* * Audio-Graph-Card */ - for_each_child_of_node(np, ports) { + for_each_child_of_node_scoped(np, ports) { node = rsnd_pick_endpoint_node_for_ports(ports, np); if (!node) continue; @@ -1299,7 +1291,6 @@ audio_graph: i++; if (i >= RSND_MAX_COMPONENT) { dev_info(dev, "reach to max component\n"); - of_node_put(ports); break; } } @@ -1505,10 +1496,9 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) dai_i = 0; if (is_graph) { struct device_node *dai_np_port; - struct device_node *ports; struct device_node *dai_np; - for_each_child_of_node(np, ports) { + for_each_child_of_node_scoped(np, ports) { dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np); if (!dai_np_port) continue; @@ -1525,14 +1515,11 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) } } } else { - struct device_node *node; - struct device_node *dai_np; - - for_each_child_of_node(np, node) { + for_each_child_of_node_scoped(np, node) { if (!of_node_name_eq(node, RSND_NODE_DAI)) continue; - for_each_child_of_node(node, dai_np) { + for_each_child_of_node_scoped(node, dai_np) { __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) { rdai = rsnd_rdai_get(priv, dai_i); diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c index a26ec7b780cd6..bd4c61f9fb3c3 100644 --- a/sound/soc/renesas/rcar/ctu.c +++ b/sound/soc/renesas/rcar/ctu.c @@ -316,7 +316,6 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) int rsnd_ctu_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ctu *ctu; struct clk *clk; @@ -344,7 +343,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ctu = rsnd_ctu_get(priv, i); /* @@ -357,16 +356,13 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_ctu_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, clk, RSND_MOD_CTU, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_ctu_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c index 2342bbb6fe92e..2035ce06fe4c4 100644 --- a/sound/soc/renesas/rcar/dma.c +++ b/sound/soc/renesas/rcar/dma.c @@ -194,14 +194,12 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *nam struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct dma_chan *chan = NULL; - struct device_node *np; int i = 0; - for_each_child_of_node(of_node, np) { + for_each_child_of_node_scoped(of_node, np) { i = rsnd_node_fixed_index(dev, np, name, i); if (i < 0) { chan = NULL; - of_node_put(np); break; } diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c index da91dd301aabe..988cbddbc6114 100644 --- a/sound/soc/renesas/rcar/dvc.c +++ b/sound/soc/renesas/rcar/dvc.c @@ -324,7 +324,6 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) int rsnd_dvc_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dvc *dvc; struct clk *clk; @@ -352,7 +351,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { dvc = rsnd_dvc_get(priv, i); snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", @@ -361,16 +360,13 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_dvc_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_dvc_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c index 024d91cc87484..aea74e7033051 100644 --- a/sound/soc/renesas/rcar/mix.c +++ b/sound/soc/renesas/rcar/mix.c @@ -288,7 +288,6 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) int rsnd_mix_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mix *mix; struct clk *clk; @@ -316,7 +315,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) i = 0; ret = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { mix = rsnd_mix_get(priv, i); snprintf(name, MIX_NAME_SIZE, "%s.%d", @@ -325,16 +324,13 @@ int rsnd_mix_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_mix_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, clk, RSND_MOD_MIX, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_mix_probe_done; - } i++; } diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index 7d73b183bda68..f47bf38c2f940 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -715,7 +715,6 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) int rsnd_src_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_src *src; struct clk *clk; @@ -742,14 +741,13 @@ int rsnd_src_probe(struct rsnd_priv *priv) priv->src = src; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (!of_device_is_available(np)) goto skip; i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); if (i < 0) { ret = -EINVAL; - of_node_put(np); goto rsnd_src_probe_done; } @@ -761,23 +759,19 @@ int rsnd_src_probe(struct rsnd_priv *priv) src->irq = irq_of_parse_and_map(np, 0); if (!src->irq) { ret = -EINVAL; - of_node_put(np); goto rsnd_src_probe_done; } clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_src_probe_done; } ret = rsnd_mod_init(priv, rsnd_mod_get(src), &rsnd_src_ops, clk, RSND_MOD_SRC, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_src_probe_done; - } skip: i++; diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index 0c6424a1fcac0..d52056caa3ec9 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -1115,7 +1115,6 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); struct device *dev = rsnd_priv_to_dev(priv); struct device_node *node; - struct device_node *np; int i; node = rsnd_ssi_of_node(priv); @@ -1123,14 +1122,12 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, return; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = rsnd_ssi_mod_get(priv, i); @@ -1163,7 +1160,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; - struct device_node *np; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; struct clk *clk; @@ -1191,14 +1187,13 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) priv->ssi_nr = nr; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (!of_device_is_available(np)) goto skip; i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); if (i < 0) { ret = -EINVAL; - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1210,7 +1205,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) clk = devm_clk_get(dev, name); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1223,7 +1217,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ssi->irq = irq_of_parse_and_map(np, 0); if (!ssi->irq) { ret = -EINVAL; - of_node_put(np); goto rsnd_ssi_probe_done; } @@ -1234,10 +1227,9 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, RSND_MOD_SSI, i); - if (ret) { - of_node_put(np); + if (ret) goto rsnd_ssi_probe_done; - } + skip: i++; } diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c index 665e8b2db579e..faf351126d574 100644 --- a/sound/soc/renesas/rcar/ssiu.c +++ b/sound/soc/renesas/rcar/ssiu.c @@ -478,17 +478,14 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, /* use rcar_sound,ssiu if exist */ if (node) { - struct device_node *np; int i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { struct rsnd_mod *mod; i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i); - if (i < 0) { - of_node_put(np); + if (i < 0) break; - } mod = rsnd_ssiu_mod_get(priv, i); -- GitLab From 618abc785e0cbe9993f7217c9f8ecb1cd2478e4f Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 3 Jun 2025 13:51:08 +0800 Subject: [PATCH 013/868] ASoC: meson: Use helper function for_each_child_of_node_scoped() The for_each_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202505210557.EpJig9BQ-lkp@intel.com/ Signed-off-by: Ai Chao Link: https://patch.msgid.link/20250603055109.3154061-4-aichao@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/meson/axg-card.c | 3 +-- sound/soc/meson/meson-card-utils.c | 16 +++++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index a2dfccb7990f3..b4dca80e15e40 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -222,7 +222,6 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, struct axg_dai_link_tdm_data *be) { struct axg_dai_link_tdm_mask *codec_mask; - struct device_node *np; codec_mask = devm_kcalloc(card->dev, link->num_codecs, sizeof(*codec_mask), GFP_KERNEL); @@ -231,7 +230,7 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card, be->codec_masks = codec_mask; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", &codec_mask->rx); snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 68531183fb60c..cdb759b466ad4 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -137,7 +137,6 @@ int meson_card_set_be_link(struct snd_soc_card *card, struct device_node *node) { struct snd_soc_dai_link_component *codec; - struct device_node *np; int ret, num_codecs; num_codecs = of_get_child_count(node); @@ -154,19 +153,17 @@ int meson_card_set_be_link(struct snd_soc_card *card, link->codecs = codec; link->num_codecs = num_codecs; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ret = meson_card_parse_dai(card, np, codec); - if (ret) { - of_node_put(np); + if (ret) return ret; - } codec++; } ret = meson_card_set_link_name(card, link, node, "be"); if (ret) - dev_err(card->dev, "error setting %pOFn link name\n", np); + dev_err(card->dev, "error setting %pOFn link name\n", node); return ret; } @@ -198,7 +195,6 @@ static int meson_card_add_links(struct snd_soc_card *card) { struct meson_card *priv = snd_soc_card_get_drvdata(card); struct device_node *node = card->dev->of_node; - struct device_node *np; int num, i, ret; num = of_get_child_count(node); @@ -212,12 +208,10 @@ static int meson_card_add_links(struct snd_soc_card *card) return ret; i = 0; - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { ret = priv->match_data->add_link(card, np, &i); - if (ret) { - of_node_put(np); + if (ret) return ret; - } i++; } -- GitLab From 111a2c8ab462d77d1519b71b46f13ae1b46920b4 Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 3 Jun 2025 13:51:09 +0800 Subject: [PATCH 014/868] ASoC: imx-card: Use helper function for_each_child_of_node_scoped() The for_each_child_of_node_scoped() helper provides a scope-based clean-up functionality to put the device_node automatically, and as such, there is no need to call of_node_put() directly. Signed-off-by: Ai Chao Link: https://patch.msgid.link/20250603055109.3154061-5-aichao@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/fsl/imx-card.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 9e668ae68039f..ea5dbb54b5842 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -513,7 +513,6 @@ static int imx_card_parse_of(struct imx_card_data *data) struct device_node *platform = NULL; struct device_node *codec = NULL; struct device_node *cpu = NULL; - struct device_node *np; struct device *dev = card->dev; struct snd_soc_dai_link *link; struct dai_link_data *link_data; @@ -552,11 +551,10 @@ static int imx_card_parse_of(struct imx_card_data *data) link = card->dai_link; link_data = data->link_data; - for_each_child_of_node(dev->of_node, np) { + for_each_child_of_node_scoped(dev->of_node, np) { dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); if (!dlc) { - ret = -ENOMEM; - goto err_put_np; + return -ENOMEM; } link->cpus = &dlc[0]; @@ -567,8 +565,8 @@ static int imx_card_parse_of(struct imx_card_data *data) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); - goto err_put_np; + return dev_err_probe(card->dev, ret, + "error getting codec dai_link name\n"); } cpu = of_get_child_by_name(np, "cpu"); @@ -725,8 +723,7 @@ err: of_node_put(cpu); of_node_put(codec); of_node_put(platform); -err_put_np: - of_node_put(np); + return ret; } -- GitLab From 8167f4f42572818fa8153be2b03e4c2120846603 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 4 Jun 2025 02:06:48 +0000 Subject: [PATCH 015/868] ASoC: qcom: use drvdata instead of component to keep id Qcom lpass is using component->id to keep DAI ID (A). (S) static int lpass_platform_pcmops_open( sruct snd_soc_component *component, struct snd_pcm_substream *substream) { ^^^^^^^^^(B0) ... (B1) struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); (B2) struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); ... (B3) unsigned int dai_id = cpu_dai->driver->id; (A) component->id = dai_id; ... } This driver can get dai_id from substream (B0 - B3). In this driver, below functions get dai_id from component->id (A). (X) lpass_platform_pcmops_suspend() (Y) lpass_platform_pcmops_resume() (Z) lpass_platform_copy() Here, (Z) can get it from substream (B0 - B3), don't need to use component->id (A). On suspend/resume (X)(Y), dai_id can only be obtained from component->id (A), because there is no substream (B0) in function parameter. But, component->id (A) itself should not be used for such purpose. It is intilialized at snd_soc_component_initialize(), and parsed its ID (= component->id) from device name (a). int snd_soc_component_initialize(...) { ... if (!component->name) { (a) component->name = fmt_single_name(dev, &component->id); ... ^^^^^^^^^^^^^ } ... } Unfortunately, current code is broken to start with. There are many regmaps that the driver cares about, however its only managing one (either dp or i2s) in component suspend/resume path. I2S regmap is mandatory however other regmaps are setup based on flags like "hdmi_port_enable" and "codec_dma_enable". Correct thing for suspend/resume path to handle is by checking these flags, instead of using component->id. Signed-off-by: Srinivas Kandagatla Suggested-by: Kuninori Morimoto Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87a56ouuob.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 9946f12254b39..b456e096f138f 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -202,7 +202,6 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, struct regmap *map; unsigned int dai_id = cpu_dai->driver->id; - component->id = dai_id; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1190,13 +1189,14 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component) { struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct regmap *map; - unsigned int dai_id = component->id; - if (dai_id == LPASS_DP_RX) + if (drvdata->hdmi_port_enable) { map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; + regcache_cache_only(map, true); + regcache_mark_dirty(map); + } + map = drvdata->lpaif_map; regcache_cache_only(map, true); regcache_mark_dirty(map); @@ -1207,14 +1207,19 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component) { struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct regmap *map; - unsigned int dai_id = component->id; + int ret; - if (dai_id == LPASS_DP_RX) + if (drvdata->hdmi_port_enable) { map = drvdata->hdmiif_map; - else - map = drvdata->lpaif_map; + regcache_cache_only(map, false); + ret = regcache_sync(map); + if (ret) + return ret; + } + map = drvdata->lpaif_map; regcache_cache_only(map, false); + return regcache_sync(map); } @@ -1224,7 +1229,9 @@ static int lpass_platform_copy(struct snd_soc_component *component, unsigned long bytes) { struct snd_pcm_runtime *rt = substream->runtime; - unsigned int dai_id = component->id; + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + unsigned int dai_id = cpu_dai->driver->id; int ret = 0; void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos + -- GitLab From 6ada7351af0c4e295739adfa2c4b780c037b3d27 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 4 Jun 2025 02:06:57 +0000 Subject: [PATCH 016/868] ASoC: soc-core: save ID if param was set in fmt_single_name() fmt_single_name() requests "ind *id" and not allow NULL for it. But sometimes we don't need it. Allow NULL. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878qm8uunz.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 67bebc339148b..ecea2dddbe9a9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2604,6 +2604,7 @@ static char *fmt_single_name(struct device *dev, int *id) const char *devname = dev_name(dev); char *found, *name; unsigned int id1, id2; + int __id; if (devname == NULL) return NULL; @@ -2616,10 +2617,10 @@ static char *fmt_single_name(struct device *dev, int *id) found = strstr(name, dev->driver->name); if (found) { /* get ID */ - if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { + if (sscanf(&found[strlen(dev->driver->name)], ".%d", &__id) == 1) { /* discard ID from name if ID == -1 */ - if (*id == -1) + if (__id == -1) found[strlen(dev->driver->name)] = '\0'; } @@ -2627,16 +2628,19 @@ static char *fmt_single_name(struct device *dev, int *id) } else if (sscanf(name, "%x-%x", &id1, &id2) == 2) { /* create unique ID number from I2C addr and bus */ - *id = ((id1 & 0xffff) << 16) + id2; + __id = ((id1 & 0xffff) << 16) + id2; devm_kfree(dev, name); /* sanitize component name for DAI link creation */ name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname); } else { - *id = 0; + __id = 0; } + if (id) + *id = __id; + return name; } -- GitLab From 267be32b0a7b70cc777f8a46f0904c92c0521d89 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 4 Jun 2025 02:07:22 +0000 Subject: [PATCH 017/868] ASoC: remove component->id No one is using component->id. One idea is we can re-use it as serial number for component. But we have no usage, so far. Let's just remove it for now. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/877c1suuna.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 1 - sound/soc/soc-core.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 61534ac0edd1d..2caa807c6249c 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -206,7 +206,6 @@ struct snd_soc_component_driver { struct snd_soc_component { const char *name; - int id; const char *name_prefix; struct device *dev; struct snd_soc_card *card; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ecea2dddbe9a9..cfafdabcdc888 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2835,7 +2835,7 @@ int snd_soc_component_initialize(struct snd_soc_component *component, mutex_init(&component->io_mutex); if (!component->name) { - component->name = fmt_single_name(dev, &component->id); + component->name = fmt_single_name(dev, NULL); if (!component->name) { dev_err(dev, "ASoC: Failed to allocate name\n"); return -ENOMEM; -- GitLab From 10cf8f6be63f2acfd3d366d917f1af6625cd9124 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:50 +0200 Subject: [PATCH 018/868] ASoC: codecs: wcd937x: Simplify with devm_regulator_bulk_get_enable() Drop separate regulator get and enable in probe() path with devm_regulator_bulk_get_enable(), which simplifies cleanup paths and device remove(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-1-64d3cb60313b@linaro.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 31 +++++++++---------------------- sound/soc/codecs/wcd937x.h | 1 - 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index b9df58b86ce95..92765a8693fbf 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -90,7 +90,6 @@ struct wcd937x_priv { struct irq_domain *virq; struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD937X_MAX_BULK_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD937X_MAX_MICBIAS]; @@ -113,6 +112,10 @@ struct wcd937x_priv { atomic_t ana_clk_count; }; +static const char * const wcd937x_supplies[] = { + "vdd-rxtx", "vdd-px", "vdd-mic-bias", "vdd-buck", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -2934,18 +2937,10 @@ static int wcd937x_probe(struct platform_device *pdev) cfg = &wcd937x->mbhc_cfg; cfg->swap_gnd_mic = wcd937x_swap_gnd_mic; - wcd937x->supplies[0].supply = "vdd-rxtx"; - wcd937x->supplies[1].supply = "vdd-px"; - wcd937x->supplies[2].supply = "vdd-mic-bias"; - wcd937x->supplies[3].supply = "vdd-buck"; - - ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd937x_supplies), + wcd937x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); - if (ret) - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd937x_dt_parse_micbias_info(dev, wcd937x); @@ -2962,13 +2957,13 @@ static int wcd937x_probe(struct platform_device *pdev) ret = wcd937x_add_slave_components(wcd937x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd937x_reset(wcd937x); ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -2978,25 +2973,17 @@ static int wcd937x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); - - return ret; } static void wcd937x_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct wcd937x_priv *wcd937x = dev_get_drvdata(dev); component_master_del(&pdev->dev, &wcd937x_comp_ops); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); - - regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies); } #if defined(CONFIG_OF) diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 4ef57c496c37c..3ab21bb5846e2 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -487,7 +487,6 @@ #define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31) #define WCD937X_MAX_MICBIAS 3 -#define WCD937X_MAX_BULK_SUPPLY 4 #define WCD937X_MAX_SWR_CH_IDS 15 #define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1) -- GitLab From a7ee107c2dc382d28794d7b254d0b4de2a75dff2 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:51 +0200 Subject: [PATCH 019/868] ASoC: codecs: wcd938x: Simplify with devm_regulator_bulk_get_enable() Drop separate regulator get and enable in probe() path with devm_regulator_bulk_get_enable(), which simplifies cleanup paths and device remove(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-2-64d3cb60313b@linaro.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index d9b61eab029af..9bee50d37619b 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -26,7 +26,6 @@ #include "wcd938x.h" #define WCD938X_MAX_MICBIAS (4) -#define WCD938X_MAX_SUPPLY (4) #define WCD938X_MBHC_MAX_BUTTONS (8) #define TX_ADC_MAX (4) @@ -161,7 +160,6 @@ struct wcd938x_priv { struct irq_domain *virq; struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD938X_MAX_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD938X_MAX_MICBIAS]; @@ -188,6 +186,10 @@ struct wcd938x_priv { bool mux_setup_done; }; +static const char * const wcd938x_supplies[] = { + "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000); @@ -3292,20 +3294,10 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; - wcd938x->supplies[0].supply = "vdd-rxtx"; - wcd938x->supplies[1].supply = "vdd-io"; - wcd938x->supplies[2].supply = "vdd-buck"; - wcd938x->supplies[3].supply = "vdd-mic-bias"; - - ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd938x_supplies), + wcd938x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - if (ret) { - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); - } + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd938x_dt_parse_micbias_info(dev, wcd938x); @@ -3569,13 +3561,13 @@ static int wcd938x_probe(struct platform_device *pdev) ret = wcd938x_add_slave_components(wcd938x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd938x_reset(wcd938x); ret = component_master_add_with_match(dev, &wcd938x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -3585,12 +3577,6 @@ static int wcd938x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); - - return ret; } static void wcd938x_remove(struct platform_device *pdev) @@ -3606,9 +3592,6 @@ static void wcd938x_remove(struct platform_device *pdev) if (wcd938x->us_euro_mux && wcd938x->mux_setup_done) mux_control_deselect(wcd938x->us_euro_mux); - - regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); - regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); } #if defined(CONFIG_OF) -- GitLab From cc50d176d0d8b38df2ae119310970f793cb5e756 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:52 +0200 Subject: [PATCH 020/868] ASoC: codecs: wcd939x: Simplify with devm_regulator_bulk_get_enable() Drop separate regulator get and enable in probe() path with devm_regulator_bulk_get_enable(), which simplifies cleanup paths and device remove(). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-3-64d3cb60313b@linaro.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 067d23c7ecf90..95cd88a37acb5 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -32,7 +32,6 @@ #include "wcd939x.h" #define WCD939X_MAX_MICBIAS (4) -#define WCD939X_MAX_SUPPLY (4) #define WCD939X_MBHC_MAX_BUTTONS (8) #define TX_ADC_MAX (4) #define WCD_MBHC_HS_V_MAX 1600 @@ -192,7 +191,6 @@ struct wcd939x_priv { struct irq_domain *virq; struct regmap_irq_chip *wcd_regmap_irq_chip; struct regmap_irq_chip_data *irq_chip; - struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY]; struct snd_soc_jack *jack; unsigned long status_mask; s32 micb_ref[WCD939X_MAX_MICBIAS]; @@ -213,6 +211,10 @@ struct wcd939x_priv { bool ldoh; }; +static const char * const wcd939x_supplies[] = { + "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", +}; + static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); @@ -3244,20 +3246,10 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device return dev_err_probe(dev, ret, "Failed to get reset gpio\n"); } - wcd939x->supplies[0].supply = "vdd-rxtx"; - wcd939x->supplies[1].supply = "vdd-io"; - wcd939x->supplies[2].supply = "vdd-buck"; - wcd939x->supplies[3].supply = "vdd-mic-bias"; - - ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd939x_supplies), + wcd939x_supplies); if (ret) - return dev_err_probe(dev, ret, "Failed to get supplies\n"); - - ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - if (ret) { - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); - return dev_err_probe(dev, ret, "Failed to enable supplies\n"); - } + return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); wcd939x_dt_parse_micbias_info(dev, wcd939x); @@ -3629,17 +3621,17 @@ static int wcd939x_probe(struct platform_device *pdev) ret = wcd939x_add_typec(wcd939x, dev); if (ret) - goto err_disable_regulators; + return ret; ret = wcd939x_add_slave_components(wcd939x, dev, &match); if (ret) - goto err_disable_regulators; + return ret; wcd939x_reset(wcd939x); ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match); if (ret) - goto err_disable_regulators; + return ret; pm_runtime_set_autosuspend_delay(dev, 1000); pm_runtime_use_autosuspend(dev); @@ -3649,27 +3641,17 @@ static int wcd939x_probe(struct platform_device *pdev) pm_runtime_idle(dev); return 0; - -err_disable_regulators: - regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); - - return ret; } static void wcd939x_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); component_master_del(dev, &wcd939x_comp_ops); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_dont_use_autosuspend(dev); - - regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies); - regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies); } #if defined(CONFIG_OF) -- GitLab From 1a134881a8f861bfc996d77d3eee9017a95eb5d7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:53 +0200 Subject: [PATCH 021/868] ASoC: codecs: wcd939x: Simplify return from devm_gpiod_get() error No need to store devm_gpiod_get() error code in temporary variable. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-4-64d3cb60313b@linaro.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 95cd88a37acb5..9592462f2d6e3 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -3241,10 +3241,9 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device int ret; wcd939x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(wcd939x->reset_gpio)) { - ret = PTR_ERR(wcd939x->reset_gpio); - return dev_err_probe(dev, ret, "Failed to get reset gpio\n"); - } + if (IS_ERR(wcd939x->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd939x->reset_gpio), + "Failed to get reset gpio\n"); ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd939x_supplies), wcd939x_supplies); -- GitLab From 7d648206bae8e7fd3d67481751a76dcb0a299eb8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:54 +0200 Subject: [PATCH 022/868] ASoC: dt-bindings: qcom,wcd939x: Document missing VDD_PX supply Document VDD_PX supply on WCD9390 and WCD9395 audio codecs, which was missed in original posting. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-5-64d3cb60313b@linaro.org Acked-by: "Rob Herring (Arm)" Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml index c69291f4d575c..85283f94465d4 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd939x.yaml @@ -45,6 +45,9 @@ properties: purpose of handling altmode muxing and orientation switching to detect and enable Audio Accessory Mode. + vdd-px-supply: + description: A reference to the 1.2V PX supply + required: - compatible -- GitLab From b9ecde0bcf6a99a3ff08496d4ba90a385ebbfd68 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 May 2025 12:49:55 +0200 Subject: [PATCH 023/868] ASoC: codecs: wcd939x: Add VDD_PX supply Device has also VDD_PX supply, which should be acquired by the driver. Regulator framework will provide a dummy supply, thus the change is compatible with older DTS. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250526-b4-asoc-wcd9395-vdd-px-v1-6-64d3cb60313b@linaro.org Reviewed-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 9592462f2d6e3..690832037b5dc 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -212,7 +212,7 @@ struct wcd939x_priv { }; static const char * const wcd939x_supplies[] = { - "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", + "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", "vdd-px", }; static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); -- GitLab From c2bde4666d229d155de5f9b0aa0676804b32fa7a Mon Sep 17 00:00:00 2001 From: Bram Vlerick Date: Wed, 28 May 2025 14:10:08 +0200 Subject: [PATCH 024/868] ASoC: tas571x: add support for tas5753 Add support for tas5753, device is similar to tas5733 but with added headphone / line driver. Signed-off-by: Bram Vlerick Link: https://patch.msgid.link/20250528-asoc-tas5753-support-v1-1-a50c3f6734ee@openpixelsystems.org Reviewed-by: Krzysztof Kozlowski Acked-by: Peter Korsgaard Signed-off-by: Mark Brown --- sound/soc/codecs/tas571x.c | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 6bf37c77f0a77..41d73f470f8b1 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -839,6 +839,56 @@ static const struct tas571x_chip tas5733_chip = { .vol_reg_size = 2, }; +static const struct reg_default tas5753_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x41}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0x03ff}, + {TAS571X_CH1_VOL_REG, 0x00c0}, + {TAS571X_CH2_VOL_REG, 0x00c0}, + {TAS571X_CH3_VOL_REG, 0x00c0}, + {TAS571X_VOL_CFG_REG, 0xf0}, + {TAS571X_MODULATION_LIMIT_REG, 0x01}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x57}, + {TAS571X_INPUT_MUX_REG, 0x00017772}, + {TAS571X_PWM_MUX_REG, 0x01021345}, + {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00}, + {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000}, + {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00}, + {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000}, +}; + +static const struct regmap_config tas5753_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5753_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5753_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +static const struct tas571x_chip tas5753_chip = { + .supply_names = tas5721_supply_names, + .num_supply_names = ARRAY_SIZE(tas5721_supply_names), + .controls = tas5733_controls, + .num_controls = ARRAY_SIZE(tas5733_controls), + .regmap_config = &tas5753_regmap_config, + .vol_reg_size = 2, +}; + static const struct tas571x_chip tas5721_chip = { .supply_names = tas5721_supply_names, .num_supply_names = ARRAY_SIZE(tas5721_supply_names), @@ -1007,6 +1057,7 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = { { .compatible = "ti,tas5719", .data = &tas5717_chip, }, { .compatible = "ti,tas5721", .data = &tas5721_chip, }, { .compatible = "ti,tas5733", .data = &tas5733_chip, }, + { .compatible = "ti,tas5753", .data = &tas5753_chip, }, { } }; MODULE_DEVICE_TABLE(of, tas571x_of_match); @@ -1018,6 +1069,7 @@ static const struct i2c_device_id tas571x_i2c_id[] = { { "tas5719", (kernel_ulong_t) &tas5717_chip }, { "tas5721", (kernel_ulong_t) &tas5721_chip }, { "tas5733", (kernel_ulong_t) &tas5733_chip }, + { "tas5753", (kernel_ulong_t) &tas5753_chip }, { } }; MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); -- GitLab From f6f914893d478b7ba08e5c375de1ced16deb5e92 Mon Sep 17 00:00:00 2001 From: Bram Vlerick Date: Wed, 28 May 2025 14:10:09 +0200 Subject: [PATCH 025/868] ASoC: dt-bindings: tas57xx: add tas5753 compatibility Add tas5753 to ti,tas57xx devicetree bindings. Signed-off-by: Bram Vlerick Link: https://patch.msgid.link/20250528-asoc-tas5753-support-v1-2-a50c3f6734ee@openpixelsystems.org Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ti,tas57xx.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml index 74f7d02b424b9..0b013a34e2c10 100644 --- a/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas57xx.yaml @@ -18,6 +18,7 @@ properties: - ti,tas5719 - ti,tas5721 - ti,tas5733 + - ti,tas5753 reg: maxItems: 1 @@ -98,6 +99,7 @@ allOf: contains: enum: - ti,tas5721 + - ti,tas5753 then: properties: HPVDD-supply: false -- GitLab From 5dc302d00807b8916992dd25a7a22b78d07dcd03 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 6 Jun 2025 17:18:21 +0800 Subject: [PATCH 026/868] ASOC: rockchip: fix capture stream handling in rockchip_sai_xfer_stop Correcting the capture stream handling which was incorrectly setting playback=true for capture streams. The original code mistakenly set playback=true for capture streams, causing incorrect behavior. Fixes: cc78d1eaabad ("ASoC: rockchip: add Serial Audio Interface (SAI) driver") Signed-off-by: Pei Xiao Tested-by: Nicolas Frattaroli Acked-by: Nicolas Frattaroli Link: https://patch.msgid.link/c374aae92c177aaf42c0f1371eccdbc7e9615786.1749201126.git.xiaopei01@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 602f1ddfad006..916af63f1c2cb 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -378,19 +378,9 @@ static void rockchip_sai_xfer_start(struct rk_sai_dev *sai, int stream) static void rockchip_sai_xfer_stop(struct rk_sai_dev *sai, int stream) { unsigned int msk = 0, val = 0, clr = 0; - bool playback; - bool capture; - - if (stream < 0) { - playback = true; - capture = true; - } else if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - playback = true; - capture = false; - } else { - playback = true; - capture = false; - } + bool capture = stream == SNDRV_PCM_STREAM_CAPTURE || stream < 0; + bool playback = stream == SNDRV_PCM_STREAM_PLAYBACK || stream < 0; + /* could be <= 0 but we don't want to depend on enum values */ if (playback) { msk |= SAI_XFER_TXS_MASK; -- GitLab From 03b778d1994827ea5cc971dbdfbb457bbb7bfa5d Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Fri, 6 Jun 2025 17:18:22 +0800 Subject: [PATCH 027/868] ASOC: rockchip: Use helper function devm_clk_get_enabled() Since commit 7ef9651e9792 ("clk: Provide new devm_clk helpers for prepared and enabled clocks"), devm_clk_get() and clk_prepare_enable() can now be replaced by devm_clk_get_enabled() when driver enables the clocks for the whole lifetime of the device. Moreover, it is no longer necessary to unprepare and disable the clocks explicitly. Signed-off-by: Pei Xiao Acked-by: Nicolas Frattaroli Tested-by: Nicolas Frattaroli Link: https://patch.msgid.link/84bc40641d05596f1edf4f01d1e6aea16bdbeeb5.1749201126.git.xiaopei01@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 35 +++++++++---------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 916af63f1c2cb..0b9f54102d693 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -1427,43 +1427,32 @@ static int rockchip_sai_probe(struct platform_device *pdev) if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); - if (ret) { + if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to request irq %d\n", irq); - } } else { dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); } sai->mclk = devm_clk_get(&pdev->dev, "mclk"); - if (IS_ERR(sai->mclk)) { + if (IS_ERR(sai->mclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk), "Failed to get mclk\n"); - } - sai->hclk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(sai->hclk)) { + sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk"); + if (IS_ERR(sai->hclk)) return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk), "Failed to get hclk\n"); - } - - ret = clk_prepare_enable(sai->hclk); - if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to enable hclk\n"); regmap_read(sai->regmap, SAI_VERSION, &sai->version); ret = rockchip_sai_init_dai(sai, res, &dai); - if (ret) { - dev_err(&pdev->dev, "Failed to initialize DAI: %d\n", ret); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to initialize DAI\n"); ret = rockchip_sai_parse_paths(sai, node); - if (ret) { - dev_err(&pdev->dev, "Failed to parse paths: %d\n", ret); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n"); /* * From here on, all register accesses need to be wrapped in @@ -1474,10 +1463,8 @@ static int rockchip_sai_probe(struct platform_device *pdev) devm_pm_runtime_enable(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); ret = rockchip_sai_runtime_resume(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "Failed to resume device: %pe\n", ERR_PTR(ret)); - goto err_disable_hclk; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n"); ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { @@ -1504,8 +1491,6 @@ err_runtime_suspend: /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */ if (pm_runtime_put(&pdev->dev)) rockchip_sai_runtime_suspend(&pdev->dev); -err_disable_hclk: - clk_disable_unprepare(sai->hclk); return ret; } -- GitLab From cecec195b2930fc45d71e29f66a66f0c83e41468 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 30 May 2025 16:21:18 +0200 Subject: [PATCH 028/868] ASoC: codecs: rt5640: Drop dummy register names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registers 0xfa and 0xfb represent "General Control 1" and "General Control 2" respectively. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20250530142120.2944095-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 38 +++++++++++++++++++------------------- sound/soc/codecs/rt5640.h | 6 +++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 21a18012b4c0d..265b30856faf4 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -193,7 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg) case RT5640_PRIV_DATA: case RT5640_PGM_REG_ARR1: case RT5640_PGM_REG_ARR3: - case RT5640_DUMMY2: + case RT5640_GCTL2: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: case RT5640_VENDOR_ID2: @@ -325,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg) case RT5640_HP_CALIB2: case RT5640_SV_ZCD1: case RT5640_SV_ZCD2: - case RT5640_DUMMY1: - case RT5640_DUMMY2: + case RT5640_GCTL1: + case RT5640_GCTL2: case RT5640_DUMMY3: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: @@ -423,7 +423,7 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = { SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 127, 0, adc_vol_tlv), - SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1, + SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1, RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1), SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, @@ -1969,7 +1969,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); - snd_soc_component_update_bits(component, RT5640_DUMMY1, + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0x1); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); @@ -1979,7 +1979,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_OFF: snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004); snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0); snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000); snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000); snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000); @@ -2328,12 +2328,12 @@ static void rt5640_jack_work(struct work_struct *work) jack_type |= SND_JACK_MICROPHONE; /* headphone jack */ - val = snd_soc_component_read(component, RT5640_DUMMY2); + val = snd_soc_component_read(component, RT5640_GCTL2); hda_hp_plugged = !(val & (0x1 << 11)); dev_dbg(component->dev, "headphone jack status %d\n", hda_hp_plugged); - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, (0x1 << 10), !hda_hp_plugged << 10); if (hda_hp_plugged) @@ -2504,7 +2504,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); - snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41); + snd_soc_component_write(component, RT5640_GCTL1, 0x3f41); rt5640_set_ovcd_params(component); @@ -2519,7 +2519,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | RT5640_JD2_EN); } else { @@ -2527,7 +2527,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR | RT5640_JD_P_INV); else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) - snd_soc_component_update_bits(component, RT5640_DUMMY2, + snd_soc_component_update_bits(component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV | @@ -2596,7 +2596,7 @@ static void rt5640_enable_hda_jack_detect( snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); /* Select JD2 for Headphone */ - snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + snd_soc_component_update_bits(component, RT5640_GCTL2, 0x1100, 0x1100); /* Selecting GPIO01 as an interrupt */ snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, @@ -2606,7 +2606,7 @@ static void rt5640_enable_hda_jack_detect( snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x400, 0x0); snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG, @@ -2668,7 +2668,7 @@ static int rt5640_probe(struct snd_soc_component *component) snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); - snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301); + snd_soc_component_update_bits(component, RT5640_GCTL1, 0x0301, 0x0301); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00); @@ -2719,7 +2719,7 @@ static int rt5640_probe(struct snd_soc_component *component) RT5640_IN_DF2, RT5640_IN_DF2); if (device_property_read_bool(component->dev, "realtek,lout-differential")) - snd_soc_component_update_bits(component, RT5640_DUMMY1, + snd_soc_component_update_bits(component, RT5640_GCTL1, RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF); if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", @@ -2829,12 +2829,12 @@ static int rt5640_resume(struct snd_soc_component *component) if (rt5640->jack) { if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { snd_soc_component_update_bits(component, - RT5640_DUMMY2, 0x1100, 0x1100); + RT5640_GCTL2, 0x1100, 0x1100); } else { if (rt5640->jd_inverted) { if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) snd_soc_component_update_bits( - component, RT5640_DUMMY2, + component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK, RT5640_IRQ_JD2_NOR | @@ -2843,7 +2843,7 @@ static int rt5640_resume(struct snd_soc_component *component) } else { if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) snd_soc_component_update_bits( - component, RT5640_DUMMY2, + component, RT5640_GCTL2, RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK | RT5640_JD2_MASK, @@ -3026,7 +3026,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); - regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, + regmap_update_bits(rt5640->regmap, RT5640_GCTL1, RT5640_MCLK_DET, RT5640_MCLK_DET); rt5640->hp_mute = true; diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 94b9a502f7f9d..8a12cee76bdc6 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -139,8 +139,8 @@ #define RT5640_SV_ZCD1 0xd9 #define RT5640_SV_ZCD2 0xda /* Dummy Register */ -#define RT5640_DUMMY1 0xfa -#define RT5640_DUMMY2 0xfb +#define RT5640_GCTL1 0xfa +#define RT5640_GCTL2 0xfb #define RT5640_DUMMY3 0xfc @@ -1986,7 +1986,7 @@ #define RT5640_M_MONO_ADC_R_SFT 12 #define RT5640_MCLK_DET (0x1 << 11) -/* General Control 1 (0xfb) */ +/* General Control 2 (0xfb) */ #define RT5640_IRQ_JD2_MASK (0x1 << 12) #define RT5640_IRQ_JD2_SFT 12 #define RT5640_IRQ_JD2_BP (0x0 << 12) -- GitLab From 19f971057b2d7b99c80530ec1052b45de236a8da Mon Sep 17 00:00:00 2001 From: Xinxin Wan Date: Fri, 30 May 2025 16:21:19 +0200 Subject: [PATCH 029/868] ASoC: codecs: rt5640: Retry DEVICE_ID verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be more resilient to codec-detection failures when the hardware powers on slowly, add retry mechanism to the device verification check. Similar pattern is found throughout a number of Realtek codecs. Our tests show that 60ms delay is sufficient to address readiness issues on rt5640 chip. Reviewed-by: Amadeusz Sławiński Reviewed-by: Cezary Rojewski Signed-off-by: Xinxin Wan Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20250530142120.2944095-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 265b30856faf4..f50e771db24b0 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -3013,6 +3013,11 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) } regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + if (val != RT5640_DEVICE_ID) { + usleep_range(60000, 100000); + regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + } + if (val != RT5640_DEVICE_ID) { dev_err(&i2c->dev, "Device with ID register %#x is not rt5640/39\n", val); -- GitLab From c95e925daa434ee1a40a86aec6476ce588e4bd77 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 30 May 2025 16:21:20 +0200 Subject: [PATCH 030/868] ASoC: Intel: avs: Add rt5640 machine board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support connection between Intel AudioDSP and Realek 5640 codec implement avs_rt5640 machine board driver. The codec chip is located on I2C bus and the streaming occurs over I2S interface. A number of such devices can be connected simultaneously to the platform. Majority of the board's behavior is inherited from existing representatives such as avs_rt274. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20250530142120.2944095-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/board_selection.c | 27 +++ sound/soc/intel/avs/boards/Kconfig | 12 ++ sound/soc/intel/avs/boards/Makefile | 2 + sound/soc/intel/avs/boards/rt5640.c | 270 ++++++++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 sound/soc/intel/avs/boards/rt5640.c diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 673ccf1620230..fb49167f5fc4b 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -308,6 +308,33 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { }, .tplg_filename = "rt1308-tplg.bin", }, + { + .id = "10EC5640", + .uid = "1", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt5640-tplg.bin", + }, + { + .id = "10EC5640", + .uid = "3", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5640-tplg.bin", + }, + { + .id = "10EC5640", + .uid = "2", + .drv_name = "avs_rt5640", + .mach_params = { + .i2s_link_mask = AVS_SSP(2), + }, + .tplg_filename = "rt5640-tplg.bin", + }, { .id = "ESSX8336", .drv_name = "avs_es8336", diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 8b654181004e7..82f50207bb2ff 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -153,6 +153,18 @@ config SND_SOC_INTEL_AVS_MACH_RT5514 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_RT5640 + tristate "rt5640 in I2S mode" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT5640 + help + This adds support for ASoC machine board connecting AVS with RT5640, + components representing Intel AudioDSP and Realtek 5640 codec respectively. + The codec chip is present on I2C bus and the streaming occurs over I2S + interface. + Say Y or m if you have such a device. + config SND_SOC_INTEL_AVS_MACH_RT5663 tristate "rt5663 in I2S mode" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index a95256b94dc8b..46ef1babda34b 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -15,6 +15,7 @@ snd-soc-avs-rt274-y := rt274.o snd-soc-avs-rt286-y := rt286.o snd-soc-avs-rt298-y := rt298.o snd-soc-avs-rt5514-y := rt5514.o +snd-soc-avs-rt5640-y := rt5640.o snd-soc-avs-rt5663-y := rt5663.o snd-soc-avs-rt5682-y := rt5682.o snd-soc-avs-ssm4567-y := ssm4567.o @@ -34,6 +35,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c new file mode 100644 index 0000000000000..706b84ffe1ef0 --- /dev/null +++ b/sound/soc/intel/avs/boards/rt5640.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022-2025 Intel Corporation +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include +#include +#include +#include "../../../codecs/rt5640.h" +#include "../utils.h" + +#define AVS_RT5640_MCLK_HZ 19200000 +#define RT5640_CODEC_DAI "rt5640-aif1" + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + { "Headphone Jack", NULL, "HPOR" }, + { "Headphone Jack", NULL, "HPOL" }, + { "IN2P", NULL, "Mic Jack" }, + { "IN2P", NULL, "MICBIAS1" }, + { "Speaker", NULL, "SPOLP" }, + { "Speaker", NULL, "SPOLN" }, + { "Speaker", NULL, "SPORP" }, + { "Speaker", NULL, "SPORN" }, +}; + +static const struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + struct snd_soc_card *card = runtime->card; + struct snd_soc_jack_pin *pins; + struct snd_soc_jack *jack; + int num_pins, ret; + + jack = snd_soc_card_get_drvdata(card); + num_pins = ARRAY_SIZE(card_headset_pins); + + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins, + num_pins); + if (ret) + return ret; + + snd_soc_component_set_jack(codec_dai->component, jack, NULL); + card->dapm.idle_bias_off = true; + + return 0; +} + +static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + + snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */ + if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) + snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int avs_rt5640_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0); + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ, + params_rate(params) * 512); + if (ret < 0) { + dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret); + return ret; + } + + ret = rt5640_sel_asrc_clk_src(codec_dai->component, + RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER | + RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER | + RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER, + RT5640_CLK_SEL_ASRC); + if (ret) + dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops avs_rt5640_ops = { + .hw_params = avs_rt5640_hw_params, +}; + +static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot, + struct snd_soc_acpi_mach *mach, + struct snd_soc_dai_link **dai_link) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + u32 uid = 0; + int ret; + + if (mach->uid) { + ret = kstrtou32(mach->uid, 0, &uid); + if (ret) + return ret; + uid--; /* 0-based indexing. */ + } + + dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + dl->name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); + dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + if (!dl->name || !dl->cpus || !dl->codecs) + return -ENOMEM; + + dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot)); + dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid); + dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI); + if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) + return -ENOMEM; + + platform->name = dev_name(dev); + dl->num_cpus = 1; + dl->num_codecs = 1; + dl->platforms = platform; + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + dl->init = avs_rt5640_codec_init; + dl->exit = avs_rt5640_codec_exit; + dl->be_hw_params_fixup = avs_rt5640_be_fixup; + dl->ops = &avs_rt5640_ops; + dl->nonatomic = 1; + dl->no_pcm = 1; + + *dai_link = dl; + + return 0; +} + +static int avs_card_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); + + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_card_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI); + struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); + + return snd_soc_component_set_jack(codec_dai->component, jack, NULL); +} + +static int avs_rt5640_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_link; + struct device *dev = &pdev->dev; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + struct snd_soc_jack *jack; + int ssp_port, tdm_slot, ret; + + mach = dev_get_platdata(dev); + + ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot); + if (ret) + return ret; + + ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link); + if (ret) { + dev_err(dev, "Failed to create dai link: %d", ret); + return ret; + } + + jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL); + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!jack || !card) + return -ENOMEM; + + if (mach->uid) { + card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid); + if (!card->name) + return -ENOMEM; + } else { + card->name = "AVS I2S ALC5640"; + } + card->driver_name = "avs_rt5640"; + card->long_name = card->name; + card->dev = dev; + card->owner = THIS_MODULE; + card->suspend_pre = avs_card_suspend_pre; + card->resume_post = avs_card_resume_post; + card->dai_link = dai_link; + card->num_links = 1; + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + snd_soc_card_set_drvdata(card, jack); + + return devm_snd_soc_register_deferrable_card(dev, card); +} + +static const struct platform_device_id avs_rt5640_driver_ids[] = { + { + .name = "avs_rt5640", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids); + +static struct platform_driver avs_rt5640_driver = { + .probe = avs_rt5640_probe, + .driver = { + .name = "avs_rt5640", + .pm = &snd_soc_pm_ops, + }, + .id_table = avs_rt5640_driver_ids, +}; + +module_platform_driver(avs_rt5640_driver); + +MODULE_DESCRIPTION("Intel rt5640 machine driver"); +MODULE_LICENSE("GPL"); -- GitLab From bb4a0f497bc19558ba7fe9feac814286fc7ebe85 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 23 May 2025 14:18:12 +0200 Subject: [PATCH 031/868] ASoC: codecs: lpass: Drop unused AIF_INVALID first DAI identifier All four Qualcomm SoC macro codecs define DAI IDs in an enum starting with AIF_INVALID=0, which is nowhere used in the code thus actual DAI IDs start from 1. Drivers do not have their own of_xlate_dai_name(), thus snd_soc_get_dlc() expects the DTS to start numbering DAIs from 0, which creates confusing debugging scenario, e.g. DTS should use <&lpass_wsamacro 2> for WSA_MACRO_AIF_VI with dai->id=3. This also wastes some space, because drivers allocate few arrays for all DAIs and basically the [0] is never used. Drop the confusing first AIF_INVALID DAI identifier so the enum with DAI IDs will start from 0. This has little functional impact and does not affect the ABI, except saving a few bytes of memory per driver. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250523121811.380045-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-rx-macro.c | 1 - sound/soc/codecs/lpass-tx-macro.c | 1 - sound/soc/codecs/lpass-va-macro.c | 1 - sound/soc/codecs/lpass-wsa-macro.c | 1 - 4 files changed, 4 deletions(-) diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 45a6b83808b27..238dbdb46c18d 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -619,7 +619,6 @@ static struct interp_sample_rate sr_val_tbl[] = { }; enum { - RX_MACRO_AIF_INVALID = 0, RX_MACRO_AIF1_PB, RX_MACRO_AIF2_PB, RX_MACRO_AIF3_PB, diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 27bae58f40725..40d79bee45844 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -208,7 +208,6 @@ #define MCLK_FREQ 19200000 enum { - TX_MACRO_AIF_INVALID = 0, TX_MACRO_AIF1_CAP, TX_MACRO_AIF2_CAP, TX_MACRO_AIF3_CAP, diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 74ada6e775260..a49551f3fb29a 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -165,7 +165,6 @@ static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); enum { - VA_MACRO_AIF_INVALID = 0, VA_MACRO_AIF1_CAP, VA_MACRO_AIF2_CAP, VA_MACRO_AIF3_CAP, diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index c1fb71cfb5d07..da6adb3de21d7 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -369,7 +369,6 @@ static struct interp_sample_rate int_mix_sample_rate_val[] = { }; enum { - WSA_MACRO_AIF_INVALID = 0, WSA_MACRO_AIF1_PB, WSA_MACRO_AIF_MIX1_PB, WSA_MACRO_AIF_VI, -- GitLab From 039de8f598dd7f060c8fde2498a7163315466f6d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 5 Jun 2025 01:43:37 +0000 Subject: [PATCH 032/868] ASoC: hdac_hdmi: remove hdac_hdmi_jack[_port]_init() No one is using hdac_hdmi_jack[_port]_init(). Remove it and its related functions. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6v3j73q.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 297 ----------------------------------- sound/soc/codecs/hdac_hdmi.h | 10 -- 2 files changed, 307 deletions(-) delete mode 100644 sound/soc/codecs/hdac_hdmi.h diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 1139a2754ca33..e05f0bf91fa52 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -25,7 +25,6 @@ #include #include #include "../../hda/local.h" -#include "hdac_hdmi.h" #define NAME_SIZE 32 @@ -1431,122 +1430,6 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) } -static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_pcm *pcm; - struct hdac_hdmi_port *port; - struct hdac_hdmi_eld *eld; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = 0; - - pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); - if (!pcm) { - dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, - kcontrol->id.device); - return 0; - } - - if (list_empty(&pcm->port_list)) { - dev_dbg(component->dev, "%s: empty port list, device %d\n", - __func__, kcontrol->id.device); - return 0; - } - - mutex_lock(&hdmi->pin_mutex); - - list_for_each_entry(port, &pcm->port_list, head) { - eld = &port->eld; - - if (eld->eld_valid) { - uinfo->count = eld->eld_size; - break; - } - } - - mutex_unlock(&hdmi->pin_mutex); - - return 0; -} - -static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_hdmi_pcm *pcm; - struct hdac_hdmi_port *port; - struct hdac_hdmi_eld *eld; - - memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); - - pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); - if (!pcm) { - dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, - kcontrol->id.device); - return 0; - } - - if (list_empty(&pcm->port_list)) { - dev_dbg(component->dev, "%s: empty port list, device %d\n", - __func__, kcontrol->id.device); - return 0; - } - - mutex_lock(&hdmi->pin_mutex); - - list_for_each_entry(port, &pcm->port_list, head) { - eld = &port->eld; - - if (!eld->eld_valid) - continue; - - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || - eld->eld_size > ELD_MAX_SIZE) { - mutex_unlock(&hdmi->pin_mutex); - - dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", - __func__, kcontrol->id.device, eld->eld_size); - snd_BUG(); - return -EINVAL; - } - - memcpy(ucontrol->value.bytes.data, eld->eld_buffer, - eld->eld_size); - break; - } - - mutex_unlock(&hdmi->pin_mutex); - - return 0; -} - -static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) -{ - struct snd_kcontrol *kctl; - struct snd_kcontrol_new hdmi_eld_ctl = { - .access = SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "ELD", - .info = hdac_hdmi_eld_ctl_info, - .get = hdac_hdmi_eld_ctl_get, - .device = pcm->pcm_id, - }; - - /* add ELD ctl with the device number corresponding to the PCM stream */ - kctl = snd_ctl_new1(&hdmi_eld_ctl, component); - if (!kctl) - return -ENOMEM; - - pcm->eld_ctl = kctl; - - return snd_ctl_add(component->card->snd_card, kctl); -} - static const struct snd_soc_dai_ops hdmi_dai_ops = { .startup = hdac_hdmi_pcm_open, .shutdown = hdac_hdmi_pcm_close, @@ -1754,186 +1637,6 @@ static struct drm_audio_component_audio_ops aops = { .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; -static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, - int device) -{ - struct snd_soc_pcm_runtime *rtd; - - for_each_card_rtds(card, rtd) { - if (rtd->pcm && (rtd->pcm->device == device)) - return rtd->pcm; - } - - return NULL; -} - -/* create jack pin kcontrols */ -static int create_fill_jack_kcontrols(struct snd_soc_card *card, - struct hdac_device *hdev) -{ - struct hdac_hdmi_pin *pin; - struct snd_kcontrol_new *kc; - char *name; - int i = 0, j; - struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); - struct snd_soc_component *component = hdmi->component; - - kc = devm_kcalloc(component->dev, hdmi->num_ports, - sizeof(*kc), GFP_KERNEL); - - if (!kc) - return -ENOMEM; - - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - name = devm_kasprintf(component->dev, GFP_KERNEL, - "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - if (!name) - return -ENOMEM; - - kc[i].name = devm_kasprintf(component->dev, GFP_KERNEL, - "%s Switch", name); - if (!kc[i].name) - return -ENOMEM; - - kc[i].private_value = (unsigned long)name; - kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc[i].access = 0; - kc[i].info = snd_soc_dapm_info_pin_switch; - kc[i].put = snd_soc_dapm_put_pin_switch; - kc[i].get = snd_soc_dapm_get_pin_switch; - i++; - } - } - - return snd_soc_add_card_controls(card, kc, i); -} - -int hdac_hdmi_jack_port_init(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm) -{ - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_device *hdev = hdmi->hdev; - struct hdac_hdmi_pin *pin; - struct snd_soc_dapm_widget *widgets; - struct snd_soc_dapm_route *route; - char w_name[NAME_SIZE]; - int i = 0, j, ret; - - widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*widgets), GFP_KERNEL); - - if (!widgets) - return -ENOMEM; - - route = devm_kcalloc(dapm->dev, hdmi->num_ports, - sizeof(*route), GFP_KERNEL); - if (!route) - return -ENOMEM; - - /* create Jack DAPM widget */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) { - snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", - pin->nid, pin->ports[j].id); - - ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], - snd_soc_dapm_spk, NULL, - w_name, NULL, NULL, 0, NULL, 0); - if (ret < 0) - return ret; - - pin->ports[j].jack_pin = widgets[i].name; - pin->ports[j].dapm = dapm; - - /* add to route from Jack widget to output */ - hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, - NULL, pin->ports[j].output_pin, NULL); - - i++; - } - } - - /* Add Route from Jack widget to the output widget */ - ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); - if (ret < 0) - return ret; - - ret = snd_soc_dapm_new_widgets(dapm->card); - if (ret < 0) - return ret; - - /* Add Jack Pin switch Kcontrol */ - ret = create_fill_jack_kcontrols(dapm->card, hdev); - - if (ret < 0) - return ret; - - /* default set the Jack Pin switch to OFF */ - list_for_each_entry(pin, &hdmi->pin_list, head) { - for (j = 0; j < pin->num_ports; j++) - snd_soc_dapm_disable_pin(pin->ports[j].dapm, - pin->ports[j].jack_pin); - } - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); - -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, - struct snd_soc_jack *jack) -{ - struct snd_soc_component *component = dai->component; - struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); - struct hdac_device *hdev = hdmi->hdev; - struct hdac_hdmi_pcm *pcm; - struct snd_pcm *snd_pcm; - int err; - - /* - * this is a new PCM device, create new pcm and - * add to the pcm list - */ - pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); - if (!pcm) - return -ENOMEM; - pcm->pcm_id = device; - pcm->cvt = hdmi->dai_map[dai->id].cvt; - pcm->jack_event = 0; - pcm->jack = jack; - mutex_init(&pcm->lock); - INIT_LIST_HEAD(&pcm->port_list); - snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); - if (snd_pcm) { - err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); - if (err < 0) { - dev_err(&hdev->dev, - "chmap control add failed with err: %d for pcm: %d\n", - err, device); - return err; - } - } - - /* add control for ELD Bytes */ - err = hdac_hdmi_create_eld_ctl(component, pcm); - if (err < 0) { - dev_err(&hdev->dev, - "eld control add failed with err: %d for pcm: %d\n", - err, device); - return err; - } - - list_add_tail(&pcm->head, &hdmi->pcm_list); - - return 0; -} -EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); - static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) { diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h deleted file mode 100644 index 493fa3b4ef753..0000000000000 --- a/sound/soc/codecs/hdac_hdmi.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __HDAC_HDMI_H__ -#define __HDAC_HDMI_H__ - -int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, - struct snd_soc_jack *jack); - -int hdac_hdmi_jack_port_init(struct snd_soc_component *component, - struct snd_soc_dapm_context *dapm); -#endif /* __HDAC_HDMI_H__ */ -- GitLab From f4c77d5af0a9cd0ee22617baa8b49d0e151fbda7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 6 Jun 2025 01:59:15 +0000 Subject: [PATCH 033/868] ASoC: soc-dai: tidyup return value of snd_soc_xlate_tdm_slot_mask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7f1186a8d738661 ("ASoC: soc-dai: check return value at snd_soc_dai_set_tdm_slot()") checks return value of xlate_tdm_slot_mask() (A1)(A2). /* * ... (Y) * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, * @rx_mask and @slot_width will be ignored. * ... */ int snd_soc_dai_set_tdm_slot(...) { ... if (...) (A1) ret = dai->driver->ops->xlate_tdm_slot_mask(...); else (A2) ret = snd_soc_xlate_tdm_slot_mask(...); if (ret) goto err; ... } snd_soc_xlate_tdm_slot_mask() (A2) will return -EINVAL if slots was 0 (X), but snd_soc_dai_set_tdm_slot() allow to use it (Y). (A) static int snd_soc_xlate_tdm_slot_mask(...) { ... if (!slots) (X) return -EINVAL; ... } Call xlate_tdm_slot_mask() only if slots was non zero. Reported-by: Giedrius Trainavičius Closes: https://lore.kernel.org/r/CAMONXLtSL7iKyvH6w=CzPTxQdBECf++hn8RKL6Y4=M_ou2YHow@mail.gmail.com Fixes: 7f1186a8d738661 ("ASoC: soc-dai: check return value at snd_soc_dai_set_tdm_slot()") Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/8734cdfx59.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dai.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index a210089747d00..32f46a38682b7 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -259,13 +259,15 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, &rx_mask, }; - if (dai->driver->ops && - dai->driver->ops->xlate_tdm_slot_mask) - ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - else - ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - if (ret) - goto err; + if (slots) { + if (dai->driver->ops && + dai->driver->ops->xlate_tdm_slot_mask) + ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + else + ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + if (ret) + goto err; + } for_each_pcm_streams(stream) snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); -- GitLab From db1a7a6f28b84c5ccd84a800cd4827b5599c95b9 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 28 May 2025 12:57:54 -0400 Subject: [PATCH 034/868] ASoC: dt-bindings: covert mxs-audio-sgtl5000.txt to yaml format Convert mxs-audio-sgtl5000.txt to yaml format. Additional changes: - Add compatible string: bluegiga,apx4devkit-sgtl5000 denx,m28evk-sgtl5000 fsl,imx28-mbmx28lc-sgtl500 - Remove audio-routing from required list. Signed-off-by: Frank Li Reviewed-by: "Rob Herring (Arm)" Link: https://patch.msgid.link/20250528165755.692264-1-Frank.Li@nxp.com Signed-off-by: Mark Brown --- .../sound/fsl,mxs-audio-sgtl5000.yaml | 81 +++++++++++++++++++ .../bindings/sound/mxs-audio-sgtl5000.txt | 42 ---------- 2 files changed, 81 insertions(+), 42 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml delete mode 100644 Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt diff --git a/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml b/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml new file mode 100644 index 0000000000000..d12774b42a112 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,mxs-audio-sgtl5000.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/fsl,mxs-audio-sgtl5000.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale MXS audio complex with SGTL5000 codec + +maintainers: + - Frank Li + +properties: + compatible: + items: + - enum: + - bluegiga,apx4devkit-sgtl5000 + - denx,m28evk-sgtl5000 + - fsl,imx28-evk-sgtl5000 + - fsl,imx28-mbmx28lc-sgtl5000 + - fsl,imx28-tx28-sgtl5000 + - const: fsl,mxs-audio-sgtl5000 + + model: + $ref: /schemas/types.yaml#/definitions/string + description: The user-visible name of this sound complex + + saif-controllers: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: The phandle list of the MXS SAIF controller + + audio-codec: + $ref: /schemas/types.yaml#/definitions/phandle + description: The phandle of the SGTL5000 audio codec + + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: | + A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's + source. Valid names could be power supplies, SGTL5000 + pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + SGTL5000 pins: + * MIC_IN + * LINE_IN + * HP_OUT + * LINE_OUT + + Board connectors: + * Mic Jack + * Line In Jack + * Headphone Jack + * Line Out Jack + * Ext Spk + +required: + - compatible + - saif-controllers + - audio-codec + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "fsl,imx28-evk-sgtl5000", "fsl,mxs-audio-sgtl5000"; + model = "imx28-evk-sgtl5000"; + saif-controllers = <&saif0 &saif1>; + audio-codec = <&sgtl5000>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + }; diff --git a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt b/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt deleted file mode 100644 index 4eb980bd02874..0000000000000 --- a/Documentation/devicetree/bindings/sound/mxs-audio-sgtl5000.txt +++ /dev/null @@ -1,42 +0,0 @@ -* Freescale MXS audio complex with SGTL5000 codec - -Required properties: -- compatible : "fsl,mxs-audio-sgtl5000" -- model : The user-visible name of this sound complex -- saif-controllers : The phandle list of the MXS SAIF controller -- audio-codec : The phandle of the SGTL5000 audio codec -- audio-routing : A list of the connections between audio components. - Each entry is a pair of strings, the first being the - connection's sink, the second being the connection's - source. Valid names could be power supplies, SGTL5000 - pins, and the jacks on the board: - - Power supplies: - * Mic Bias - - SGTL5000 pins: - * MIC_IN - * LINE_IN - * HP_OUT - * LINE_OUT - - Board connectors: - * Mic Jack - * Line In Jack - * Headphone Jack - * Line Out Jack - * Ext Spk - -Example: - -sound { - compatible = "fsl,imx28-evk-sgtl5000", - "fsl,mxs-audio-sgtl5000"; - model = "imx28-evk-sgtl5000"; - saif-controllers = <&saif0 &saif1>; - audio-codec = <&sgtl5000>; - audio-routing = - "MIC_IN", "Mic Jack", - "Mic Jack", "Mic Bias", - "Headphone Jack", "HP_OUT"; -}; -- GitLab From ac209bde018fd320b79976657a44c23113181af6 Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Fri, 23 May 2025 21:11:11 +0800 Subject: [PATCH 035/868] ASoC: tas2781: Drop the unnecessary symbol imply The unnecessary symbols for imply are SND_SOC_TAS2781_COMLIB, SND_SOC_TAS2781_COMLIB_I2C, and SND_SOC_TAS2781_FMWLIB. They all used for library compiling. All the symbols in the imply are used for codec driver compiling. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20250523131111.1884-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 126f897312d47..496a3e4b276f0 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -263,9 +263,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 - imply SND_SOC_TAS2781_COMLIB - imply SND_SOC_TAS2781_COMLIB_I2C - imply SND_SOC_TAS2781_FMWLIB imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 imply SND_SOC_TAS571X -- GitLab From c8c4694ede7ed42d8d4db0e8927dea9839a3e248 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 28 May 2025 21:45:02 +0200 Subject: [PATCH 036/868] regmap: kunit: Constify regmap_range_cfg array Static 'struct regmap_range_cfg' array is not modified so can be changed to const for more safety. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250528194501.567366-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-kunit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 64ea340950b6a..95c5bf2a78eeb 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -736,7 +736,7 @@ static void stride(struct kunit *test) } } -static struct regmap_range_cfg test_range = { +static const struct regmap_range_cfg test_range = { .selector_reg = 1, .selector_mask = 0xff, -- GitLab From c266209eaef4fef863363557817f7d6a68314321 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 24 May 2025 18:10:38 +0200 Subject: [PATCH 037/868] regulator: tps6594-regulator: Constify struct tps6594_regulator_irq_type 'struct tps6594_regulator_irq_type' are not modified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 25645 14480 64 40189 9cfd drivers/regulator/tps6594-regulator.o After: ===== text data bss dec hex filename 27949 12176 64 40189 9cfd drivers/regulator/tps6594-regulator.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/1446fb1938f3f38115be3e53f5dda3c8bb0ba5a1.1748103005.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 60 +++++++++++++-------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index ac53792e3fede..0193efb5dffa9 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -56,7 +56,7 @@ struct tps6594_regulator_irq_type { unsigned long event; }; -static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { { TPS6594_IRQ_NAME_VCCA_OV, "VCCA", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_VCCA_UV, "VCCA", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_VMON1_OV, "VMON1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, @@ -69,7 +69,7 @@ static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { REGULATOR_EVENT_OVER_VOLTAGE_WARN }, }; -static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { { TPS65224_IRQ_NAME_VCCA_UVOV, "VCCA", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, { TPS65224_IRQ_NAME_VMON1_UVOV, "VMON1", "voltage out of range", @@ -80,13 +80,13 @@ static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { struct tps6594_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; struct regulator_dev *rdev; }; struct tps6594_ext_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; }; #define TPS6594_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \ @@ -262,7 +262,7 @@ static const struct regulator_desc tps65224_buck_regs[] = { 4, 0, 0, NULL, 0, 0), }; -static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { { TPS6594_IRQ_NAME_BUCK1_OV, "BUCK1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK1_UV, "BUCK1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK1_SC, "BUCK1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -270,7 +270,7 @@ static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { { TPS6594_IRQ_NAME_BUCK2_OV, "BUCK2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK2_UV, "BUCK2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK2_SC, "BUCK2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -278,7 +278,7 @@ static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { { TPS6594_IRQ_NAME_BUCK3_OV, "BUCK3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK3_UV, "BUCK3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK3_SC, "BUCK3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -286,7 +286,7 @@ static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { { TPS6594_IRQ_NAME_BUCK4_OV, "BUCK4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK4_UV, "BUCK4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK4_SC, "BUCK4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -294,7 +294,7 @@ static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { { TPS6594_IRQ_NAME_BUCK5_OV, "BUCK5", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK5_UV, "BUCK5", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK5_SC, "BUCK5", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -302,7 +302,7 @@ static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { { TPS6594_IRQ_NAME_LDO1_OV, "LDO1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO1_UV, "LDO1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO1_SC, "LDO1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -310,7 +310,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { { TPS6594_IRQ_NAME_LDO2_OV, "LDO2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO2_UV, "LDO2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO2_SC, "LDO2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -318,7 +318,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { { TPS6594_IRQ_NAME_LDO3_OV, "LDO3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO3_UV, "LDO3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO3_SC, "LDO3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -326,7 +326,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { { TPS6594_IRQ_NAME_LDO4_OV, "LDO4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO4_UV, "LDO4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO4_SC, "LDO4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -334,42 +334,42 @@ static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { { TPS65224_IRQ_NAME_BUCK1_UVOV, "BUCK1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { { TPS65224_IRQ_NAME_BUCK2_UVOV, "BUCK2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { { TPS65224_IRQ_NAME_BUCK3_UVOV, "BUCK3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { { TPS65224_IRQ_NAME_BUCK4_UVOV, "BUCK4", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { { TPS65224_IRQ_NAME_LDO1_UVOV, "LDO1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { { TPS65224_IRQ_NAME_LDO2_UVOV, "LDO2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { { TPS65224_IRQ_NAME_LDO3_UVOV, "LDO3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck1_irq_types, tps6594_buck2_irq_types, tps6594_buck3_irq_types, @@ -377,21 +377,21 @@ static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck5_irq_types, }; -static struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { tps6594_ldo1_irq_types, tps6594_ldo2_irq_types, tps6594_ldo3_irq_types, tps6594_ldo4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { tps65224_buck1_irq_types, tps65224_buck2_irq_types, tps65224_buck3_irq_types, tps65224_buck4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { tps65224_ldo1_irq_types, tps65224_ldo2_irq_types, tps65224_ldo3_irq_types, @@ -516,11 +516,11 @@ static irqreturn_t tps6594_regulator_irq_handler(int irq, void *data) static int tps6594_request_reg_irqs(struct platform_device *pdev, struct regulator_dev *rdev, struct tps6594_regulator_irq_data *irq_data, - struct tps6594_regulator_irq_type *regs_irq_types, + const struct tps6594_regulator_irq_type *regs_irq_types, size_t interrupt_cnt, int *irq_idx) { - struct tps6594_regulator_irq_type *irq_type; + const struct tps6594_regulator_irq_type *irq_type; struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); size_t j; int irq; @@ -558,8 +558,8 @@ static int tps6594_regulator_probe(struct platform_device *pdev) struct regulator_config config = {}; struct tps6594_regulator_irq_data *irq_data; struct tps6594_ext_regulator_irq_data *irq_ext_reg_data; - struct tps6594_regulator_irq_type *irq_type; - struct tps6594_regulator_irq_type *irq_types; + const struct tps6594_regulator_irq_type *irq_type; + const struct tps6594_regulator_irq_type *irq_types; bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; @@ -573,9 +573,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) unsigned int irq_count; unsigned int multi_phase_cnt; size_t reg_irq_nb; - struct tps6594_regulator_irq_type **bucks_irq_types; + const struct tps6594_regulator_irq_type **bucks_irq_types; const struct regulator_desc *multi_regs; - struct tps6594_regulator_irq_type **ldos_irq_types; + const struct tps6594_regulator_irq_type **ldos_irq_types; const struct regulator_desc *ldo_regs; size_t interrupt_count; -- GitLab From 9bb3c7df546aac38ea64c736a839ef2c75297631 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 24 May 2025 18:10:39 +0200 Subject: [PATCH 038/868] regulator: tps6594-regulator: Remove a useless static qualifier There is no point in having 'npname' a static variable. So remove the static qualifier. This is cleaner and saves a few bytes. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 27949 12176 64 40189 9cfd drivers/regulator/tps6594-regulator.o After: ===== text data bss dec hex filename 27947 12112 0 40059 9c7b drivers/regulator/tps6594-regulator.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/ebc53d4049ec19796ef07e1bb734de19a2814727.1748103005.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 0193efb5dffa9..51264c869aa02 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -563,7 +563,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; - static const char *npname; + const char *npname; int error, i, irq, multi; int irq_idx = 0; int buck_idx = 0; -- GitLab From d7181a2d43cffb19f1e5c19f6d2328f190c87d70 Mon Sep 17 00:00:00 2001 From: Martijn de Gouw Date: Sun, 25 May 2025 09:18:20 +0200 Subject: [PATCH 039/868] dt-bindings: regulator: add pca9450: Add regulator-allowed-modes Make the PWM mode on the buck controllers configurable from devicetree. Some boards require forced PWM mode to keep the supply ripple within acceptable limits under light load conditions. Signed-off-by: Martijn de Gouw Acked-by: Conor Dooley Link: https://patch.msgid.link/20250525071823.819342-1-martijn.de.gouw@prodrive-technologies.com Signed-off-by: Mark Brown --- .../regulator/nxp,pca9450-regulator.yaml | 14 ++++++++++++++ .../regulator/nxp,pca9450-regulator.h | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 include/dt-bindings/regulator/nxp,pca9450-regulator.h diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml index 4ffe5c3faea07..a5486c36830f0 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml @@ -100,6 +100,15 @@ properties: PMIC default "STANDBY" state voltage in uV. Only Buck1~3 have such dvs(dynamic voltage scaling) property. + regulator-allowed-modes: + description: | + Buck regulator operating modes allowed. Valid values below. + Users should use the macros from dt-bindings/regulator/nxp,pca9450-regulator.h + 0 (PCA9450_BUCK_MODE_AUTO): Auto PFM/PWM mode + 1 (PCA9450_BUCK_MODE_FORCE_PWM): Forced PWM mode + items: + enum: [ 0, 1 ] + unevaluatedProperties: false additionalProperties: false @@ -143,6 +152,7 @@ allOf: examples: - | #include + #include i2c { #address-cells = <1>; @@ -179,6 +189,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-initial-mode = ; + regulator-allowed-modes = ; }; buck5: BUCK5 { regulator-name = "BUCK5"; @@ -186,6 +198,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-allowed-modes = ; }; buck6: BUCK6 { regulator-name = "BUCK6"; diff --git a/include/dt-bindings/regulator/nxp,pca9450-regulator.h b/include/dt-bindings/regulator/nxp,pca9450-regulator.h new file mode 100644 index 0000000000000..08434caef429f --- /dev/null +++ b/include/dt-bindings/regulator/nxp,pca9450-regulator.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Device Tree binding constants for the NXP PCA9450A/B/C PMIC regulators + */ + +#ifndef _DT_BINDINGS_REGULATORS_NXP_PCA9450_H +#define _DT_BINDINGS_REGULATORS_NXP_PCA9450_H + +/* + * Buck mode constants which may be used in devicetree properties (eg. + * regulator-initial-mode, regulator-allowed-modes). + * See the manufacturer's datasheet for more information on these modes. + */ + +#define PCA9450_BUCK_MODE_AUTO 0 +#define PCA9450_BUCK_MODE_FORCE_PWM 1 + +#endif -- GitLab From 548d770c330cd1027549947a6ea899c56b5bc4e4 Mon Sep 17 00:00:00 2001 From: Martijn de Gouw Date: Sun, 25 May 2025 09:18:21 +0200 Subject: [PATCH 040/868] regulator: pca9450: Add support for mode operations Make the PWM mode on the buck controllers configurable from devicetree. Some boards require forced PWM mode to keep the supply ripple within acceptable limits under light load conditions. Signed-off-by: Martijn de Gouw Link: https://patch.msgid.link/20250525071823.819342-2-martijn.de.gouw@prodrive-technologies.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 120 +++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 14d19a6d66557..f6faf14a9c536 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -17,12 +17,18 @@ #include #include #include +#include + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev); +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode); struct pc9450_dvs_config { unsigned int run_reg; /* dvs0 */ unsigned int run_mask; unsigned int standby_reg; /* dvs1 */ unsigned int standby_mask; + unsigned int mode_reg; /* ctrl */ + unsigned int mode_mask; }; struct pca9450_regulator_desc { @@ -80,6 +86,8 @@ static const struct regulator_ops pca9450_dvs_buck_regulator_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = regulator_set_ramp_delay_regmap, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_buck_regulator_ops = { @@ -90,6 +98,8 @@ static const struct regulator_ops pca9450_buck_regulator_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_ldo_regulator_ops = { @@ -285,7 +295,64 @@ static int pca9450_set_dvs_levels(struct device_node *np, return ret; } -static const struct pca9450_regulator_desc pca9450a_regulators[] = { +static inline unsigned int pca9450_map_mode(unsigned int mode) +{ + switch (mode) { + case PCA9450_BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + case PCA9450_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = dvs->mode_mask; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + dev_dbg(&rdev->dev, "pca9450 buck set_mode %#x, %#x, %#x\n", + dvs->mode_reg, dvs->mode_mask, val); + + return regmap_update_bits(rdev->regmap, dvs->mode_reg, + dvs->mode_mask, val); +} + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int ret = 0, regval; + + ret = regmap_read(rdev->regmap, dvs->mode_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, + "Failed to get pca9450 buck mode: %d\n", ret); + return ret; + } + + if ((regval & dvs->mode_mask) == dvs->mode_mask) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static struct pca9450_regulator_desc pca9450a_regulators[] = { { .desc = { .name = "buck1", @@ -308,12 +375,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -338,12 +408,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -368,12 +441,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK3OUT_DVS0, .run_mask = BUCK3OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK3OUT_DVS1, .standby_mask = BUCK3OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK3CTRL, + .mode_mask = BUCK3_FPWM, }, }, { @@ -393,6 +469,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -412,6 +493,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -431,6 +517,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -529,7 +620,7 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { * Buck3 removed on PCA9450B and connected with Buck1 internal for dual phase * on PCA9450C as no Buck3. */ -static const struct pca9450_regulator_desc pca9450bc_regulators[] = { +static struct pca9450_regulator_desc pca9450bc_regulators[] = { { .desc = { .name = "buck1", @@ -552,12 +643,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -582,12 +676,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -607,6 +704,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -626,6 +728,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -645,6 +752,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -739,7 +851,7 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { }, }; -static const struct pca9450_regulator_desc pca9451a_regulators[] = { +static struct pca9450_regulator_desc pca9451a_regulators[] = { { .desc = { .name = "buck1", @@ -990,7 +1102,7 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) { enum pca9450_chip_type type = (unsigned int)(uintptr_t) of_device_get_match_data(&i2c->dev); - const struct pca9450_regulator_desc *regulator_desc; + const struct pca9450_regulator_desc *regulator_desc; struct regulator_config config = { }; struct regulator_dev *ldo5; struct pca9450 *pca9450; -- GitLab From 87a14a96bc323aff824fad8cdbe61b78eff22255 Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 22 May 2025 15:51:33 +0100 Subject: [PATCH 041/868] spi: spi-fsl-dspi: Re-use one volatile regmap for both device types max_register overrides anything in the volatile ranges, so we can get away with sharing the same one for both types. In a later commit we'll add more devices so this avoids adding even more duplication. Also replace the max_register magic numbers with their register definitions so it's clearer what's going on. No functional changes. Reviewed-by: Vladimir Oltean Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-4-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 863781ba6c160..09b2b25ed2747 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1209,6 +1209,7 @@ static const struct regmap_range dspi_volatile_ranges[] = { regmap_reg_range(SPI_MCR, SPI_TCR), regmap_reg_range(SPI_SR, SPI_SR), regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), }; static const struct regmap_access_table dspi_volatile_table = { @@ -1220,31 +1221,19 @@ static const struct regmap_config dspi_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x88, + .max_register = SPI_RXFR3, .volatile_table = &dspi_volatile_table, .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }; -static const struct regmap_range dspi_xspi_volatile_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_TCR), - regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_xspi_volatile_table = { - .yes_ranges = dspi_xspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), -}; - static const struct regmap_config dspi_xspi_regmap_config[] = { { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x13c, - .volatile_table = &dspi_xspi_volatile_table, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, -- GitLab From 1672b0653212cecf11be9ef55bc2a2fabe0fa2ca Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 22 May 2025 15:51:34 +0100 Subject: [PATCH 042/868] spi: spi-fsl-dspi: Define regmaps per device Refactor the regmaps so they can be defined per device rather than programmatically. This will allow us to add two new regmaps for S32G in a later commit. No functional changes. Reviewed-by: Vladimir Oltean Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-5-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 136 ++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 62 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 09b2b25ed2747..437a8db9fa2bb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -122,6 +122,7 @@ struct fsl_dspi_devtype_data { enum dspi_trans_mode trans_mode; u8 max_clock_factor; int fifo_size; + const struct regmap_config *regmap; }; enum { @@ -137,60 +138,130 @@ enum { VF610, }; +static const struct regmap_range dspi_yes_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_MCR), + regmap_reg_range(SPI_TCR, SPI_CTAR(3)), + regmap_reg_range(SPI_SR, SPI_TXFR3), + regmap_reg_range(SPI_RXFR0, SPI_RXFR3), + regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_access_table = { + .yes_ranges = dspi_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), +}; + +static const struct regmap_range dspi_volatile_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_TCR), + regmap_reg_range(SPI_SR, SPI_SR), + regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_volatile_table = { + .yes_ranges = dspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), +}; + +enum { + DSPI_REGMAP, + DSPI_XSPI_REGMAP, + DSPI_PUSHR, +}; + +static const struct regmap_config dspi_regmap_config[] = { + [DSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_RXFR3, + .volatile_table = &dspi_volatile_table, + .rd_table = &dspi_access_table, + .wr_table = &dspi_access_table, + }, + [DSPI_XSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, + .rd_table = &dspi_access_table, + .wr_table = &dspi_access_table, + }, + [DSPI_PUSHR] = { + .name = "pushr", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x2, + }, +}; + static const struct fsl_dspi_devtype_data devtype_data[] = { [VF610] = { .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 2, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_REGMAP], }, [LS1021A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1012A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1028A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1043A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS1046A] = { /* Has A-011218 DMA erratum */ .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS2080A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LS2085A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [LX2160A] = { .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, + .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP], }, [MCF5441X] = { .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 8, .fifo_size = 16, + .regmap = &dspi_regmap_config[DSPI_REGMAP], }, }; @@ -1191,61 +1262,6 @@ static int dspi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); -static const struct regmap_range dspi_yes_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_MCR), - regmap_reg_range(SPI_TCR, SPI_CTAR(3)), - regmap_reg_range(SPI_SR, SPI_TXFR3), - regmap_reg_range(SPI_RXFR0, SPI_RXFR3), - regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_access_table = { - .yes_ranges = dspi_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), -}; - -static const struct regmap_range dspi_volatile_ranges[] = { - regmap_reg_range(SPI_MCR, SPI_TCR), - regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), - regmap_reg_range(SPI_SREX, SPI_SREX), -}; - -static const struct regmap_access_table dspi_volatile_table = { - .yes_ranges = dspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), -}; - -static const struct regmap_config dspi_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = SPI_RXFR3, - .volatile_table = &dspi_volatile_table, - .rd_table = &dspi_access_table, - .wr_table = &dspi_access_table, -}; - -static const struct regmap_config dspi_xspi_regmap_config[] = { - { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = SPI_SREX, - .volatile_table = &dspi_volatile_table, - .rd_table = &dspi_access_table, - .wr_table = &dspi_access_table, - }, - { - .name = "pushr", - .reg_bits = 16, - .val_bits = 16, - .reg_stride = 2, - .max_register = 0x2, - }, -}; - static int dspi_init(struct fsl_dspi *dspi) { unsigned int mcr; @@ -1305,7 +1321,6 @@ static int dspi_target_abort(struct spi_controller *host) static int dspi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct regmap_config *regmap_config; struct fsl_dspi_platform_data *pdata; struct spi_controller *ctlr; int ret, cs_num, bus_num = -1; @@ -1388,11 +1403,8 @@ static int dspi_probe(struct platform_device *pdev) goto out_ctlr_put; } - if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) - regmap_config = &dspi_xspi_regmap_config[0]; - else - regmap_config = &dspi_regmap_config; - dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config); + dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, + dspi->devtype_data->regmap); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(dspi->regmap)); @@ -1403,7 +1415,7 @@ static int dspi_probe(struct platform_device *pdev) if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) { dspi->regmap_pushr = devm_regmap_init_mmio( &pdev->dev, base + SPI_PUSHR, - &dspi_xspi_regmap_config[1]); + &dspi_regmap_config[DSPI_PUSHR]); if (IS_ERR(dspi->regmap_pushr)) { dev_err(&pdev->dev, "failed to init pushr regmap: %ld\n", -- GitLab From 70c0b17ee344b0c14b88e6b5b1db6abe2fa84218 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:35 +0100 Subject: [PATCH 043/868] spi: spi-fsl-dspi: Add config and regmaps for S32G platforms S32G adds SPI_{T,R}XFR4 and extends SPI_CTAR registers to 5. Add the new regmaps, configs and bits. dspi_volatile_ranges gets SPI_{T,R}XFR4 added which affects all platforms, however they are further limited by dspi_yes_ranges. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-6-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 39 +++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 437a8db9fa2bb..10e511ba1cd86 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -35,7 +35,7 @@ #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16) -#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27)) #define SPI_CTAR_CPOL BIT(26) #define SPI_CTAR_CPHA BIT(25) @@ -93,12 +93,14 @@ #define SPI_TXFR1 0x40 #define SPI_TXFR2 0x44 #define SPI_TXFR3 0x48 +#define SPI_TXFR4 0x4C #define SPI_RXFR0 0x7c #define SPI_RXFR1 0x80 #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 +#define SPI_RXFR4 0x8C -#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) #define SPI_CTARE_DTCP(x) ((x) & 0x7ff) @@ -136,6 +138,7 @@ enum { LX2160A, MCF5441X, VF610, + S32G, }; static const struct regmap_range dspi_yes_ranges[] = { @@ -147,15 +150,29 @@ static const struct regmap_range dspi_yes_ranges[] = { regmap_reg_range(SPI_SREX, SPI_SREX), }; +static const struct regmap_range s32g_dspi_yes_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_MCR), + regmap_reg_range(SPI_TCR, SPI_CTAR(5)), + regmap_reg_range(SPI_SR, SPI_TXFR4), + regmap_reg_range(SPI_RXFR0, SPI_RXFR4), + regmap_reg_range(SPI_CTARE(0), SPI_CTARE(5)), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + static const struct regmap_access_table dspi_access_table = { .yes_ranges = dspi_yes_ranges, .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges), }; +static const struct regmap_access_table s32g_dspi_access_table = { + .yes_ranges = s32g_dspi_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g_dspi_yes_ranges), +}; + static const struct regmap_range dspi_volatile_ranges[] = { regmap_reg_range(SPI_MCR, SPI_TCR), regmap_reg_range(SPI_SR, SPI_SR), - regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_PUSHR, SPI_RXFR4), regmap_reg_range(SPI_SREX, SPI_SREX), }; @@ -167,6 +184,7 @@ static const struct regmap_access_table dspi_volatile_table = { enum { DSPI_REGMAP, DSPI_XSPI_REGMAP, + S32G_DSPI_XSPI_REGMAP, DSPI_PUSHR, }; @@ -189,6 +207,15 @@ static const struct regmap_config dspi_regmap_config[] = { .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, + [S32G_DSPI_XSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_SREX, + .volatile_table = &dspi_volatile_table, + .wr_table = &s32g_dspi_access_table, + .rd_table = &s32g_dspi_access_table, + }, [DSPI_PUSHR] = { .name = "pushr", .reg_bits = 16, @@ -263,6 +290,12 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 16, .regmap = &dspi_regmap_config[DSPI_REGMAP], }, + [S32G] = { + .trans_mode = DSPI_XSPI_MODE, + .max_clock_factor = 1, + .fifo_size = 5, + .regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP], + }, }; struct fsl_dspi_dma { -- GitLab From e7397e4d3b161ed8a57648a9ac03df7902958682 Mon Sep 17 00:00:00 2001 From: Marius Trifu Date: Thu, 22 May 2025 15:51:36 +0100 Subject: [PATCH 044/868] spi: spi-fsl-dspi: Use spi_alloc_target for target spi_alloc_target should be used for target devices. This also sets ctlr->target automatically so delete that line. Signed-off-by: Marius Trifu Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-7-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 10e511ba1cd86..814a92b8064e7 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1366,7 +1366,10 @@ static int dspi_probe(struct platform_device *pdev) if (!dspi) return -ENOMEM; - ctlr = spi_alloc_host(&pdev->dev, 0); + if (of_property_read_bool(np, "spi-slave")) + ctlr = spi_alloc_target(&pdev->dev, 0); + else + ctlr = spi_alloc_host(&pdev->dev, 0); if (!ctlr) return -ENOMEM; @@ -1405,9 +1408,6 @@ static int dspi_probe(struct platform_device *pdev) of_property_read_u32(np, "bus-num", &bus_num); ctlr->bus_num = bus_num; - if (of_property_read_bool(np, "spi-slave")) - ctlr->target = true; - dspi->devtype_data = of_device_get_match_data(&pdev->dev); if (!dspi->devtype_data) { dev_err(&pdev->dev, "can't get devtype_data\n"); -- GitLab From cac7e5054115fcc41b1cb050af8e8971f7c9b22b Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:37 +0100 Subject: [PATCH 045/868] spi: spi-fsl-dspi: Avoid setup_accel logic for DMA transfers Repacking multiple smaller words into larger ones to make use of the full FIFO doesn't save anything in DMA mode, so don't bother doing it. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-8-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 814a92b8064e7..24a51267cb4d9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -850,8 +850,12 @@ static void dspi_setup_accel(struct fsl_dspi *dspi) struct spi_transfer *xfer = dspi->cur_transfer; bool odd = !!(dspi->len & 1); - /* No accel for frames not multiple of 8 bits at the moment */ - if (xfer->bits_per_word % 8) + /* + * No accel for DMA transfers or frames not multiples of 8 bits at the + * moment. + */ + if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE || + xfer->bits_per_word % 8) goto no_accel; if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) { @@ -860,10 +864,7 @@ static void dspi_setup_accel(struct fsl_dspi *dspi) dspi->oper_bits_per_word = 8; } else { /* Start off with maximum supported by hardware */ - if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) - dspi->oper_bits_per_word = 32; - else - dspi->oper_bits_per_word = 16; + dspi->oper_bits_per_word = 32; /* * And go down only if the buffer can't be sent with -- GitLab From 870d6fda18d590df88beac9b0504f810807a5ed6 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:38 +0100 Subject: [PATCH 046/868] spi: spi-fsl-dspi: Use DMA for S32G controller in target mode Switch to DMA for target mode otherwise the controller is too slow to feed TX FIFO and UNDERFLOW occurs frequently. DMA can work only with 8 and 16 bits per word. 32bits per word is not supported, this is a hardware limitation, so we keep the controller mode in TCFQ mode. Signed-off-by: Larisa Grigore Signed-off-by: Ciprian Marian Costea Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-9-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 24a51267cb4d9..db5a2ed66f68b 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -139,6 +139,7 @@ enum { MCF5441X, VF610, S32G, + S32G_TARGET, }; static const struct regmap_range dspi_yes_ranges[] = { @@ -183,6 +184,7 @@ static const struct regmap_access_table dspi_volatile_table = { enum { DSPI_REGMAP, + S32G_DSPI_REGMAP, DSPI_XSPI_REGMAP, S32G_DSPI_XSPI_REGMAP, DSPI_PUSHR, @@ -198,6 +200,15 @@ static const struct regmap_config dspi_regmap_config[] = { .rd_table = &dspi_access_table, .wr_table = &dspi_access_table, }, + [S32G_DSPI_REGMAP] = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPI_RXFR4, + .volatile_table = &dspi_volatile_table, + .wr_table = &s32g_dspi_access_table, + .rd_table = &s32g_dspi_access_table, + }, [DSPI_XSPI_REGMAP] = { .reg_bits = 32, .val_bits = 32, @@ -296,6 +307,12 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 5, .regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP], }, + [S32G_TARGET] = { + .trans_mode = DSPI_DMA_MODE, + .max_clock_factor = 1, + .fifo_size = 5, + .regmap = &dspi_regmap_config[S32G_DSPI_REGMAP], + }, }; struct fsl_dspi_dma { @@ -351,6 +368,12 @@ struct fsl_dspi { void (*dev_to_host)(struct fsl_dspi *dspi, u32 rxdata); }; +static bool is_s32g_dspi(struct fsl_dspi *data) +{ + return data->devtype_data == &devtype_data[S32G] || + data->devtype_data == &devtype_data[S32G_TARGET]; +} + static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { switch (dspi->oper_word_size) { @@ -1426,6 +1449,9 @@ static int dspi_probe(struct platform_device *pdev) dspi->pushr_tx = 0; } + if (spi_controller_is_target(ctlr) && is_s32g_dspi(dspi)) + dspi->devtype_data = &devtype_data[S32G_TARGET]; + if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else -- GitLab From c5412ec5f687732f9722bd0f94f9632ad78f4c52 Mon Sep 17 00:00:00 2001 From: Larisa Grigore Date: Thu, 22 May 2025 15:51:39 +0100 Subject: [PATCH 047/868] spi: spi-fsl-dspi: Reinitialize DSPI regs after resuming for S32G After resuming, DSPI registers (MCR and SR) need to be reinitialized for S32G platforms. Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-10-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 77 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index db5a2ed66f68b..a3efe1bd3b376 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1284,41 +1284,6 @@ static const struct of_device_id fsl_dspi_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); -#ifdef CONFIG_PM_SLEEP -static int dspi_suspend(struct device *dev) -{ - struct fsl_dspi *dspi = dev_get_drvdata(dev); - - if (dspi->irq) - disable_irq(dspi->irq); - spi_controller_suspend(dspi->ctlr); - clk_disable_unprepare(dspi->clk); - - pinctrl_pm_select_sleep_state(dev); - - return 0; -} - -static int dspi_resume(struct device *dev) -{ - struct fsl_dspi *dspi = dev_get_drvdata(dev); - int ret; - - pinctrl_pm_select_default_state(dev); - - ret = clk_prepare_enable(dspi->clk); - if (ret) - return ret; - spi_controller_resume(dspi->ctlr); - if (dspi->irq) - enable_irq(dspi->irq); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); - static int dspi_init(struct fsl_dspi *dspi) { unsigned int mcr; @@ -1354,6 +1319,48 @@ static int dspi_init(struct fsl_dspi *dspi) return 0; } +#ifdef CONFIG_PM_SLEEP +static int dspi_suspend(struct device *dev) +{ + struct fsl_dspi *dspi = dev_get_drvdata(dev); + + if (dspi->irq) + disable_irq(dspi->irq); + spi_controller_suspend(dspi->ctlr); + clk_disable_unprepare(dspi->clk); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int dspi_resume(struct device *dev) +{ + struct fsl_dspi *dspi = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + ret = clk_prepare_enable(dspi->clk); + if (ret) + return ret; + spi_controller_resume(dspi->ctlr); + + ret = dspi_init(dspi); + if (ret) { + dev_err(dev, "failed to initialize dspi during resume\n"); + return ret; + } + + if (dspi->irq) + enable_irq(dspi->irq); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); + static int dspi_target_abort(struct spi_controller *host) { struct fsl_dspi *dspi = spi_controller_get_devdata(host); -- GitLab From 0cb9ca1187b311db21288a79ec7b98121f730354 Mon Sep 17 00:00:00 2001 From: Andra-Teodora Ilie Date: Thu, 22 May 2025 15:51:40 +0100 Subject: [PATCH 048/868] spi: spi-fsl-dspi: Enable modified transfer protocol on S32G S32G supports modified transfer protocol where both host and target devices sample later in the SCK period than in Classic SPI mode to allow the logic to tolerate more delays in device pads and board traces. Set MTFE bit in MCR register for frequencies higher than 25MHz. Signed-off-by: Andra-Teodora Ilie Signed-off-by: Bogdan-Gabriel Roman Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-11-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 45 +++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index a3efe1bd3b376..01af641fa757e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -24,6 +24,7 @@ #define SPI_MCR 0x00 #define SPI_MCR_HOST BIT(31) +#define SPI_MCR_MTFE BIT(26) #define SPI_MCR_PCSIS(x) ((x) << 16) #define SPI_MCR_CLR_TXF BIT(11) #define SPI_MCR_CLR_RXF BIT(10) @@ -37,6 +38,7 @@ #define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4)) #define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27)) +#define SPI_CTAR_DBR BIT(31) #define SPI_CTAR_CPOL BIT(26) #define SPI_CTAR_CPHA BIT(25) #define SPI_CTAR_LSBFE BIT(24) @@ -111,6 +113,8 @@ #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) +#define SPI_25MHZ 25000000 + struct chip_data { u32 ctar_val; }; @@ -346,6 +350,7 @@ struct fsl_dspi { const void *tx; void *rx; u16 tx_cmd; + bool mtf_enabled; const struct fsl_dspi_devtype_data *devtype_data; struct completion xfer_done; @@ -722,7 +727,7 @@ static void dspi_release_dma(struct fsl_dspi *dspi) } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, - unsigned long clkrate) + unsigned long clkrate, bool mtf_enabled) { /* Valid baud rate pre-scaler values */ int pbr_tbl[4] = {2, 3, 5, 7}; @@ -739,7 +744,13 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, for (i = 0; i < ARRAY_SIZE(brs); i++) for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) { - scale = brs[i] * pbr_tbl[j]; + if (mtf_enabled) { + /* In MTF mode DBR=1 so frequency is doubled */ + scale = (brs[i] * pbr_tbl[j]) / 2; + } else { + scale = brs[i] * pbr_tbl[j]; + } + if (scale >= scale_needed) { if (scale < minscale) { minscale = scale; @@ -1146,6 +1157,20 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, return status; } +static int dspi_set_mtf(struct fsl_dspi *dspi) +{ + if (spi_controller_is_target(dspi->ctlr)) + return 0; + + if (dspi->mtf_enabled) + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, + SPI_MCR_MTFE); + else + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, 0); + + return 0; +} + static int dspi_setup(struct spi_device *spi) { struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller); @@ -1204,7 +1229,16 @@ static int dspi_setup(struct spi_device *spi) cs_sck_delay, sck_cs_delay); clkrate = clk_get_rate(dspi->clk); - hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate); + + if (is_s32g_dspi(dspi) && spi->max_speed_hz > SPI_25MHZ) + dspi->mtf_enabled = true; + else + dspi->mtf_enabled = false; + + dspi_set_mtf(dspi); + + hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate, + dspi->mtf_enabled); /* Set PCS to SCK delay scale values */ ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate); @@ -1226,6 +1260,9 @@ static int dspi_setup(struct spi_device *spi) SPI_CTAR_PBR(pbr) | SPI_CTAR_BR(br); + if (dspi->mtf_enabled) + chip->ctar_val |= SPI_CTAR_DBR; + if (spi->mode & SPI_LSB_FIRST) chip->ctar_val |= SPI_CTAR_LSBFE; } @@ -1352,6 +1389,8 @@ static int dspi_resume(struct device *dev) return ret; } + dspi_set_mtf(dspi); + if (dspi->irq) enable_irq(dspi->irq); -- GitLab From be47ecfecf5a6f16d028fd572410251b502692bf Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 22 May 2025 15:51:41 +0100 Subject: [PATCH 049/868] dt-bindings: spi: dspi: Add S32G support Document S32G compatible strings. 's32g2' and 's32g3' use the same driver so 's32g2' must follow 's32g3'. The SPI controller supports target mode when the "spi-slave" flag is used so add an example. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ciprian Marian Costea Signed-off-by: Larisa Grigore Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-12-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/fsl,dspi.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml index bf9cce53c48da..8dbda1ffb5ebc 100644 --- a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml @@ -23,6 +23,7 @@ properties: - fsl,ls2080a-dspi - fsl,ls2085a-dspi - fsl,lx2160a-dspi + - nxp,s32g2-dspi - items: - enum: - fsl,ls1012a-dspi @@ -37,6 +38,9 @@ properties: - items: - const: fsl,lx2160a-dspi - const: fsl,ls2085a-dspi + - items: + - const: nxp,s32g3-dspi + - const: nxp,s32g2-dspi reg: maxItems: 1 @@ -114,3 +118,17 @@ examples: spi-cs-hold-delay-ns = <50>; }; }; + # S32G3 in target mode + - | + spi@401d4000 { + compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi"; + reg = <0x401d4000 0x1000>; + interrupts = ; + clocks = <&clks 26>; + clock-names = "dspi"; + spi-num-chipselects = <8>; + bus-num = <0>; + dmas = <&edma0 0 7>, <&edma0 0 8>; + dma-names = "tx", "rx"; + spi-slave; + }; -- GitLab From 9a30e332c36c52e92e5316b4a012d45284dedeb5 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Thu, 22 May 2025 15:51:42 +0100 Subject: [PATCH 050/868] spi: spi-fsl-dspi: Enable support for S32G platforms Add compatible for S32G platforms, allowing DSPI to be used. Add a depends for ARCH_NXP which can replace LAYERSCAPE and also includes the new ARCH_S32 for S32G. Similarly, ARCH_MXC can replace SOC_VF610 || SOC_LS1021A which should avoid updating this for every new sub-platform in the future. Signed-off-by: Ciprian Marian Costea Signed-off-by: Stoica Cosmin-Stefan Signed-off-by: Dan Nica Signed-off-by: Larisa Grigore Signed-off-by: Stefan-Gabriel Mirea Signed-off-by: James Clark Link: https://patch.msgid.link/20250522-james-nxp-spi-v2-13-bea884630cfb@linaro.org Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ++-- drivers/spi/spi-fsl-dspi.c | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc36049..60eb65c927b14 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -647,10 +647,10 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select REGMAP_MMIO - depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST + depends on ARCH_MXC || ARCH_NXP || M54541x || COMPILE_TEST help This enables support for the Freescale DSPI controller in master - mode. VF610, LS1021A and ColdFire platforms uses the controller. + mode. S32, VF610, LS1021A and ColdFire platforms uses the controller. config SPI_FSL_ESPI tristate "Freescale eSPI controller" diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 01af641fa757e..04c88d090c4d1 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1316,6 +1316,9 @@ static const struct of_device_id fsl_dspi_dt_ids[] = { }, { .compatible = "fsl,lx2160a-dspi", .data = &devtype_data[LX2160A], + }, { + .compatible = "nxp,s32g2-dspi", + .data = &devtype_data[S32G], }, { /* sentinel */ } }; -- GitLab From 6c1ca9928ed48499f75101057079b92072077d44 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 25 May 2025 16:15:25 +0200 Subject: [PATCH 051/868] spi: spi-qpic-snand: use NANDC_STEP_SIZE consistently Change the qcom_spi_read_page_ecc() function to use NANDC_STEP_SIZE instead of a magic number while calculating the data size to keep it consistent with other functions like qcom_spi_program_{raw,ecc,oob} and qcom_spi_read_cw_{raw,page_oob}. No functional changes. Signed-off-by: Gabor Juhos Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20250525-qpic-snand-nandc_step_size-v1-1-6039e9bfe1c6@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 77d9cc65477ad..1e51a7ecc3d03 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -835,7 +835,7 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, int data_size, oob_size; if (i == (num_cw - 1)) { - data_size = 512 - ((num_cw - 1) << 2); + data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2); oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes; } else { -- GitLab From f73dc37ebf45573349aee0aae168e8dc3d13ecee Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 29 May 2025 19:35:44 +0200 Subject: [PATCH 052/868] spi: spi-qpic-snand: remove 'qpic_snand_op' structure The 'qpic_snand_op' structure is used only in the qcom_spi_send_cmdaddr() function as a type of a local variable. Additionally, the sole purpose of that variable is to keep some interim values before those gets passed as arguments for cpu_to_le32() calls. In order to simplify the code, remove the definition of the structure along with the local variable, and use the corresponding values directly as parameters for cpu_to_le32() calls. No functional changes intended. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250529-qpic-snand-remove-qpic_snand_op-v1-1-6e42b772d748@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 1e51a7ecc3d03..ca55f9bcd17b4 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -59,12 +59,6 @@ #define OOB_BUF_SIZE 128 #define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng) -struct qpic_snand_op { - u32 cmd_reg; - u32 addr1_reg; - u32 addr2_reg; -}; - struct snandc_read_status { __le32 snandc_flash; __le32 snandc_buffer; @@ -1294,7 +1288,6 @@ static int qcom_spi_write_page(struct qcom_nand_controller *snandc, static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) { - struct qpic_snand_op s_op = {}; u32 cmd; int ret, opcode; @@ -1302,34 +1295,24 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, if (ret < 0) return ret; - s_op.cmd_reg = cmd; - s_op.addr1_reg = op->addr.val; - s_op.addr2_reg = 0; - opcode = op->cmd.opcode; switch (opcode) { case SPINAND_WRITE_EN: return 0; case SPINAND_PROGRAM_EXECUTE: - s_op.addr1_reg = op->addr.val << 16; - s_op.addr2_reg = op->addr.val >> 16 & 0xff; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff); snandc->qspi->cmd = cpu_to_le32(cmd); return qcom_spi_program_execute(snandc, op); case SPINAND_READ: - s_op.addr1_reg = (op->addr.val << 16); - s_op.addr2_reg = op->addr.val >> 16 & 0xff; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff); snandc->qspi->cmd = cpu_to_le32(cmd); return 0; case SPINAND_ERASE: - s_op.addr2_reg = (op->addr.val >> 16) & 0xffff; - s_op.addr1_reg = op->addr.val; - snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16); - snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg); + snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16); + snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xffff); snandc->qspi->cmd = cpu_to_le32(cmd); return qcom_spi_block_erase(snandc); default: @@ -1341,10 +1324,10 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, qcom_clear_read_regs(snandc); qcom_clear_bam_transaction(snandc); - snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg); + snandc->regs->cmd = cpu_to_le32(cmd); snandc->regs->exec = cpu_to_le32(1); - snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg); - snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg); + snandc->regs->addr0 = cpu_to_le32(op->addr.val); + snandc->regs->addr1 = cpu_to_le32(0); qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); -- GitLab From 6b500757aef0b5b639253508cf93eb8134a2d340 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 28 May 2025 18:28:20 -0400 Subject: [PATCH 053/868] spi: dt-bindings: mxs-spi: allow clocks properpty Allow clocks property to fix below CHECK_DTB warnings: arch/arm/boot/dts/nxp/mxs/imx28-btt3-0.dtb: spi@80014000 (fsl,imx28-spi): Unevaluated properties are not allowed ('clocks' was unexpected) Signed-off-by: Frank Li Link: https://patch.msgid.link/20250528222821.728544-1-Frank.Li@nxp.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/mxs-spi.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.yaml b/Documentation/devicetree/bindings/spi/mxs-spi.yaml index e2512166c1cd9..0cf8e7269ba99 100644 --- a/Documentation/devicetree/bindings/spi/mxs-spi.yaml +++ b/Documentation/devicetree/bindings/spi/mxs-spi.yaml @@ -24,6 +24,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + dmas: maxItems: 1 -- GitLab From c459262159f39e6e6336797feb975799344b749b Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Mon, 26 May 2025 16:19:08 +0530 Subject: [PATCH 054/868] spi: spi-pci1xxxx: Add support for 25MHz Clock frequency in C0 Adds support for 25MHz clock frequency. Support for this frequency is added in C0. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250526104908.404564-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index c6489e90b8b9c..9a7b86a5ec75b 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -23,6 +23,7 @@ #define SYS_FREQ_DEFAULT (62500000) #define PCI1XXXX_SPI_MAX_CLOCK_HZ (30000000) +#define PCI1XXXX_SPI_CLK_25MHZ (25000000) #define PCI1XXXX_SPI_CLK_20MHZ (20000000) #define PCI1XXXX_SPI_CLK_15MHZ (15000000) #define PCI1XXXX_SPI_CLK_12MHZ (12000000) @@ -318,12 +319,14 @@ static void pci1xxxx_spi_set_cs(struct spi_device *spi, bool enable) writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); } -static u8 pci1xxxx_get_clock_div(u32 hz) +static u8 pci1xxxx_get_clock_div(struct pci1xxxx_spi *par, u32 hz) { u8 val = 0; if (hz >= PCI1XXXX_SPI_MAX_CLOCK_HZ) val = 2; + else if (par->dev_rev >= 0xC0 && hz >= PCI1XXXX_SPI_CLK_25MHZ) + val = 1; else if ((hz < PCI1XXXX_SPI_MAX_CLOCK_HZ) && (hz >= PCI1XXXX_SPI_CLK_20MHZ)) val = 3; else if ((hz < PCI1XXXX_SPI_CLK_20MHZ) && (hz >= PCI1XXXX_SPI_CLK_15MHZ)) @@ -423,7 +426,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, p->spi_xfer_in_progress = true; p->bytes_recvd = 0; - clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz); + clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz); tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; transfer_len = xfer->len; @@ -492,7 +495,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, } p->xfer = xfer; p->mode = spi->mode; - p->clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz); + p->clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz); p->bytes_recvd = 0; p->rx_buf = xfer->rx_buf; regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); -- GitLab From bca638aa737d13749a871d1a0d2ed276501ffc54 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:40 +0300 Subject: [PATCH 055/868] ALSA: usb-audio: Fix code alignment in mixer_quirks Format code to fix all alignment issues reported by checkpatch.pl: CHECK: Alignment should match open parenthesis Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-1-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 250 ++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 123 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index aad205df93b26..c05861450465a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -55,13 +55,13 @@ struct std_mono_table { * version, we keep it mono for simplicity. */ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, - unsigned int unitid, - unsigned int control, - unsigned int cmask, - int val_type, - unsigned int idx_off, - const char *name, - snd_kcontrol_tlv_rw_t *tlv_callback) + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + unsigned int idx_off, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) { struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; @@ -108,15 +108,16 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, } static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, - unsigned int unitid, - unsigned int control, - unsigned int cmask, - int val_type, - const char *name, - snd_kcontrol_tlv_rw_t *tlv_callback) + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) { return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask, - val_type, 0 /* Offset */, name, tlv_callback); + val_type, 0 /* Offset */, + name, tlv_callback); } /* @@ -129,7 +130,8 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, while (t->name != NULL) { err = snd_create_std_mono_ctl(mixer, t->unitid, t->control, - t->cmask, t->val_type, t->name, t->tlv_callback); + t->cmask, t->val_type, t->name, + t->tlv_callback); if (err < 0) return err; t++; @@ -214,7 +216,7 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb) } static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, - long count, loff_t *offset) + long count, loff_t *offset) { struct usb_mixer_interface *mixer = hw->private_data; int err; @@ -234,7 +236,7 @@ static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, } static __poll_t snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, - poll_table *wait) + poll_table *wait) { struct usb_mixer_interface *mixer = hw->private_data; @@ -310,20 +312,20 @@ static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, if (chip->usb_id == USB_ID(0x041e, 0x3042)) err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - !value, 0, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0); /* USB X-Fi S51 Pro */ if (chip->usb_id == USB_ID(0x041e, 0x30df)) err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - !value, 0, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + !value, 0, NULL, 0); else err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), 0x24, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - value, index + 2, NULL, 0); + usb_sndctrlpipe(chip->dev, 0), 0x24, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + value, index + 2, NULL, 0); snd_usb_unlock_shutdown(chip); return err; } @@ -481,9 +483,9 @@ static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, buf[0] = 0x01; buf[1] = value ? 0x02 : 0x01; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - 0x0400, 0x0e00, buf, 2); + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0x0400, 0x0e00, buf, 2); snd_usb_unlock_shutdown(chip); return err; } @@ -1021,7 +1023,7 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ /* FTU Effect switch (also used by C400/C600) */ static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { static const char *const texts[8] = { "Room 1", "Room 2", "Room 3", "Hall 1", @@ -1055,7 +1057,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer, } static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.enumerated.item[0] = kctl->private_value >> 24; return 0; @@ -1086,7 +1088,7 @@ static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list) } static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); unsigned int pval = list->kctl->private_value; @@ -1104,7 +1106,7 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, - int validx, int bUnitID) + int validx, int bUnitID) { static struct snd_kcontrol_new template = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1143,22 +1145,22 @@ static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer) for (in = 0; in < 8; in++) { cmask = BIT(in); snprintf(name, sizeof(name), - "AIn%d - Out%d Capture Volume", - in + 1, out + 1); + "AIn%d - Out%d Capture Volume", + in + 1, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } for (in = 8; in < 16; in++) { cmask = BIT(in); snprintf(name, sizeof(name), - "DIn%d - Out%d Playback Volume", - in - 7, out + 1); + "DIn%d - Out%d Playback Volume", + in - 7, out + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1219,10 +1221,10 @@ static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer) for (ch = 0; ch < 4; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Return %d Volume", ch + 1); + "Effect Return %d Volume", ch + 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - snd_usb_mixer_vol_tlv); + cmask, val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1243,20 +1245,20 @@ static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer) for (ch = 0; ch < 8; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Send AIn%d Volume", ch + 1); + "Effect Send AIn%d Volume", ch + 1); err = snd_create_std_mono_ctl(mixer, id, control, cmask, - val_type, name, - snd_usb_mixer_vol_tlv); + val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } for (ch = 8; ch < 16; ++ch) { cmask = BIT(ch); snprintf(name, sizeof(name), - "Effect Send DIn%d Volume", ch - 7); + "Effect Send DIn%d Volume", ch - 7); err = snd_create_std_mono_ctl(mixer, id, control, cmask, - val_type, name, - snd_usb_mixer_vol_tlv); + val_type, name, + snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1346,19 +1348,19 @@ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) for (out = 0; out < num_outs; out++) { if (chan < num_outs) { snprintf(name, sizeof(name), - "PCM%d-Out%d Playback Volume", - chan + 1, out + 1); + "PCM%d-Out%d Playback Volume", + chan + 1, out + 1); } else { snprintf(name, sizeof(name), - "In%d-Out%d Playback Volume", - chan - num_outs + 1, out + 1); + "In%d-Out%d Playback Volume", + chan - num_outs + 1, out + 1); } cmask = (out == 0) ? 0 : BIT(out - 1); offset = chan * num_outs; err = snd_create_std_mono_ctl_offset(mixer, id, control, - cmask, val_type, offset, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1377,7 +1379,7 @@ static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer) const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, snd_usb_mixer_vol_tlv); + name, snd_usb_mixer_vol_tlv); } /* This control needs a volume quirk, see mixer.c */ @@ -1390,7 +1392,7 @@ static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, snd_usb_mixer_vol_tlv); + name, snd_usb_mixer_vol_tlv); } /* This control needs a volume quirk, see mixer.c */ @@ -1403,7 +1405,7 @@ static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer const unsigned int cmask = 0; return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, - name, NULL); + name, NULL); } static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) @@ -1432,18 +1434,18 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) for (chan = 0; chan < num_outs + num_ins; chan++) { if (chan < num_outs) { snprintf(name, sizeof(name), - "Effect Send DOut%d", - chan + 1); + "Effect Send DOut%d", + chan + 1); } else { snprintf(name, sizeof(name), - "Effect Send AIn%d", - chan - num_outs + 1); + "Effect Send AIn%d", + chan - num_outs + 1); } cmask = (chan == 0) ? 0 : BIT(chan - 1); err = snd_create_std_mono_ctl(mixer, id, control, - cmask, val_type, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1478,14 +1480,14 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer for (chan = 0; chan < num_outs; chan++) { snprintf(name, sizeof(name), - "Effect Return %d", - chan + 1); + "Effect Return %d", + chan + 1); cmask = (chan == 0) ? 0 : BIT(chan + (chan % 2) * num_outs - 1); err = snd_create_std_mono_ctl_offset(mixer, id, control, - cmask, val_type, offset, name, - &snd_usb_mixer_vol_tlv); + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); if (err < 0) return err; } @@ -1626,7 +1628,7 @@ static const struct std_mono_table ebox44_table[] = { * */ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; @@ -1634,7 +1636,7 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; @@ -1667,13 +1669,13 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC_GET_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, - ep, - data, - sizeof(data)); + usb_rcvctrlpipe(chip->dev, 0), + UAC_GET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, + ep, + data, + sizeof(data)); if (err < 0) goto end; @@ -1700,26 +1702,26 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 2, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 2, + NULL, + 0); if (err < 0) goto end; reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20; reg |= (pval >> 12) & 0x0f; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 3, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 3, + NULL, + 0); if (err < 0) goto end; @@ -1729,7 +1731,7 @@ static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) } static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); unsigned int pval, pval_old; @@ -1756,7 +1758,7 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.iec958.status[0] = 0x0f; ucontrol->value.iec958.status[1] = 0xff; @@ -1767,7 +1769,7 @@ static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, } static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02); @@ -1785,20 +1787,20 @@ static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) return err; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), - UAC_SET_CUR, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - reg, - 9, - NULL, - 0); + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 9, + NULL, + 0); snd_usb_unlock_shutdown(chip); return err; } static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); u8 reg; @@ -1883,9 +1885,9 @@ static int snd_soundblaster_e1_switch_update(struct usb_mixer_interface *mixer, if (err < 0) return err; err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - 0x0202, 3, buff, 2); + usb_sndctrlpipe(chip->dev, 0), HID_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + 0x0202, 3, buff, 2); snd_usb_unlock_shutdown(chip); return err; } @@ -3235,7 +3237,7 @@ static int snd_rme_digiface_enum_put(struct snd_kcontrol *kcontrol, } static int snd_rme_digiface_current_sync_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { int ret = snd_rme_digiface_enum_get(kcontrol, ucontrol); @@ -3918,7 +3920,7 @@ static const struct snd_djm_device snd_djm_devices[] = { static int snd_djm_controls_info(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *info) + struct snd_ctl_elem_info *info) { unsigned long private_value = kctl->private_value; u8 device_idx = (private_value & SND_DJM_DEVICE_MASK) >> SND_DJM_DEVICE_SHIFT; @@ -3937,8 +3939,8 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl, info->value.enumerated.item = noptions - 1; name = snd_djm_get_label(device_idx, - ctl->options[info->value.enumerated.item], - ctl->wIndex); + ctl->options[info->value.enumerated.item], + ctl->wIndex); if (!name) return -EINVAL; @@ -3950,7 +3952,7 @@ static int snd_djm_controls_info(struct snd_kcontrol *kctl, } static int snd_djm_controls_update(struct usb_mixer_interface *mixer, - u8 device_idx, u8 group, u16 value) + u8 device_idx, u8 group, u16 value) { int err; const struct snd_djm_device *device = &snd_djm_devices[device_idx]; @@ -3962,13 +3964,13 @@ static int snd_djm_controls_update(struct usb_mixer_interface *mixer, if (err) return err; - err = snd_usb_ctl_msg( - mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), - USB_REQ_SET_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - device->controls[group].options[value], - device->controls[group].wIndex, - NULL, 0); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + USB_REQ_SET_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + device->controls[group].options[value], + device->controls[group].wIndex, + NULL, 0); snd_usb_unlock_shutdown(mixer->chip); return err; @@ -4009,7 +4011,7 @@ static int snd_djm_controls_resume(struct usb_mixer_elem_list *list) } static int snd_djm_controls_create(struct usb_mixer_interface *mixer, - const u8 device_idx) + const u8 device_idx) { int err, i; u16 value; @@ -4028,10 +4030,10 @@ static int snd_djm_controls_create(struct usb_mixer_interface *mixer, for (i = 0; i < device->ncontrols; i++) { value = device->controls[i].default_value; knew.name = device->controls[i].name; - knew.private_value = ( + knew.private_value = ((unsigned long)device_idx << SND_DJM_DEVICE_SHIFT) | (i << SND_DJM_GROUP_SHIFT) | - value); + value; err = snd_djm_controls_update(mixer, device_idx, i, value); if (err) return err; @@ -4098,13 +4100,15 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) break; case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ - err = snd_nativeinstruments_create_mixer(mixer, + err = snd_nativeinstruments_create_mixer(/* checkpatch hack */ + mixer, snd_nativeinstruments_ta6_mixers, ARRAY_SIZE(snd_nativeinstruments_ta6_mixers)); break; case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */ - err = snd_nativeinstruments_create_mixer(mixer, + err = snd_nativeinstruments_create_mixer(/* checkpatch hack */ + mixer, snd_nativeinstruments_ta10_mixers, ARRAY_SIZE(snd_nativeinstruments_ta10_mixers)); break; -- GitLab From df6b4dcf2e2c3b4e34c3213a575c92d0c9415d0d Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:41 +0300 Subject: [PATCH 056/868] ALSA: usb-audio: Fix whitespace & blank line issues in mixer_quirks Address all whitespace & blank line(s) related issues reported by checkpatch.pl: ERROR: trailing whitespace ERROR: space required after that ',' (ctx:VxV) WARNING: Missing a blank line after declarations CHECK: Please use a blank line after function/struct/union/enum declarations CHECK: Please don't use multiple blank lines Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-2-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index c05861450465a..ec14b5ee61c20 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -287,7 +287,7 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) mixer->rc_setup_packet->wLength = cpu_to_le16(len); usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), - (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, + (u8 *)mixer->rc_setup_packet, mixer->rc_buffer, len, snd_usb_soundblaster_remote_complete, mixer); return 0; } @@ -389,7 +389,7 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) - break; + break; knew = snd_audigy2nx_control; knew.name = snd_audigy2nx_led_names[i]; @@ -858,6 +858,7 @@ static const struct snd_kcontrol_new snd_mbox1_src_switch = { static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer) { int err; + err = add_single_ctl_with_resume(mixer, 0, snd_mbox1_clk_switch_resume, &snd_mbox1_clk_switch, NULL); @@ -871,7 +872,7 @@ static int snd_mbox1_controls_create(struct usb_mixer_interface *mixer) /* Native Instruments device quirks */ -#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) +#define _MAKE_NI_CONTROL(bRequest, wIndex) ((bRequest) << 16 | (wIndex)) static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl) @@ -2183,6 +2184,7 @@ static const u32 snd_rme_rate_table[] = { 256000, 352800, 384000, 400000, 512000, 705600, 768000, 800000 }; + /* maximum number of items for AES and S/PDIF rates for above table */ #define SND_RME_RATE_IDX_AES_SPDIF_NUM 12 @@ -3271,7 +3273,6 @@ static int snd_rme_digiface_sync_state_get(struct snd_kcontrol *kcontrol, return 0; } - static int snd_rme_digiface_format_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3283,7 +3284,6 @@ static int snd_rme_digiface_format_info(struct snd_kcontrol *kcontrol, ARRAY_SIZE(format), format); } - static int snd_rme_digiface_sync_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3566,7 +3566,6 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_A9_IDX 0x6 #define SND_DJM_V10_IDX 0x7 - #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ .name = _name, \ .options = snd_djm_opts_##suffix, \ @@ -3578,7 +3577,6 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) .controls = snd_djm_ctls_##suffix, \ .ncontrols = ARRAY_SIZE(snd_djm_ctls_##suffix) } - struct snd_djm_device { const char *name; const struct snd_djm_ctl *controls; @@ -3724,7 +3722,6 @@ static const struct snd_djm_ctl snd_djm_ctls_250mk2[] = { SND_DJM_CTL("Output 3 Playback Switch", 250mk2_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-450 static const u16 snd_djm_opts_450_cap1[] = { 0x0103, 0x0100, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d, 0x010a }; @@ -3749,7 +3746,6 @@ static const struct snd_djm_ctl snd_djm_ctls_450[] = { SND_DJM_CTL("Output 3 Playback Switch", 450_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-750 static const u16 snd_djm_opts_750_cap1[] = { 0x0101, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f }; @@ -3768,7 +3764,6 @@ static const struct snd_djm_ctl snd_djm_ctls_750[] = { SND_DJM_CTL("Input 4 Capture Switch", 750_cap4, 0, SND_DJM_WINDEX_CAP) }; - // DJM-850 static const u16 snd_djm_opts_850_cap1[] = { 0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, 0x010f }; @@ -3787,7 +3782,6 @@ static const struct snd_djm_ctl snd_djm_ctls_850[] = { SND_DJM_CTL("Input 4 Capture Switch", 850_cap4, 1, SND_DJM_WINDEX_CAP) }; - // DJM-900NXS2 static const u16 snd_djm_opts_900nxs2_cap1[] = { 0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a }; @@ -3825,7 +3819,6 @@ static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 }; static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 }; static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 }; - static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL), SND_DJM_CTL("Input 1 Capture Switch", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP), @@ -3838,7 +3831,6 @@ static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { SND_DJM_CTL("Output 3 Playback Switch", 750mk2_pb3, 2, SND_DJM_WINDEX_PB) }; - // DJM-A9 static const u16 snd_djm_opts_a9_cap_level[] = { 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 }; @@ -3867,29 +3859,35 @@ static const struct snd_djm_ctl snd_djm_ctls_a9[] = { static const u16 snd_djm_opts_v10_cap_level[] = { 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 }; + static const u16 snd_djm_opts_v10_cap1[] = { 0x0103, 0x0100, 0x0102, 0x0106, 0x0110, 0x0107, 0x0108, 0x0109, 0x010a, 0x0121, 0x0122 }; + static const u16 snd_djm_opts_v10_cap2[] = { 0x0200, 0x0202, 0x0206, 0x0210, 0x0207, 0x0208, 0x0209, 0x020a, 0x0221, 0x0222 }; + static const u16 snd_djm_opts_v10_cap3[] = { 0x0303, 0x0300, 0x0302, 0x0306, 0x0310, 0x0307, 0x0308, 0x0309, 0x030a, 0x0321, 0x0322 }; + static const u16 snd_djm_opts_v10_cap4[] = { 0x0403, 0x0400, 0x0402, 0x0406, 0x0410, 0x0407, 0x0408, 0x0409, 0x040a, 0x0421, 0x0422 }; + static const u16 snd_djm_opts_v10_cap5[] = { 0x0500, 0x0502, 0x0506, 0x0510, 0x0507, 0x0508, 0x0509, 0x050a, 0x0521, 0x0522 }; + static const u16 snd_djm_opts_v10_cap6[] = { 0x0603, 0x0600, 0x0602, 0x0606, 0x0610, 0x0607, @@ -3918,7 +3916,6 @@ static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_V10_IDX] = SND_DJM_DEVICE(v10), }; - static int snd_djm_controls_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *info) { @@ -4356,4 +4353,3 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, (cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME)) snd_fix_plt_name(mixer->chip, &kctl->id); } - -- GitLab From fd3ab72e42e9871a9902b945a2bf8bb87b49c718 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:42 +0300 Subject: [PATCH 057/868] ALSA: usb-audio: Avoid precedence issues in mixer_quirks macros Fix all macro related issues identified by checkpatch.pl: CHECK: Macro argument 'x' may be better as '(x)' to avoid precedence issues Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-3-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ec14b5ee61c20..19abd709c4d73 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2156,15 +2156,15 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) #define SND_RME_CLK_FREQMUL_SHIFT 18 #define SND_RME_CLK_FREQMUL_MASK 0x7 #define SND_RME_CLK_SYSTEM(x) \ - ((x >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK) + (((x) >> SND_RME_CLK_SYSTEM_SHIFT) & SND_RME_CLK_SYSTEM_MASK) #define SND_RME_CLK_AES(x) \ - ((x >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) + (((x) >> SND_RME_CLK_AES_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) #define SND_RME_CLK_SPDIF(x) \ - ((x >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) + (((x) >> SND_RME_CLK_SPDIF_SHIFT) & SND_RME_CLK_AES_SPDIF_MASK) #define SND_RME_CLK_SYNC(x) \ - ((x >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK) + (((x) >> SND_RME_CLK_SYNC_SHIFT) & SND_RME_CLK_SYNC_MASK) #define SND_RME_CLK_FREQMUL(x) \ - ((x >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK) + (((x) >> SND_RME_CLK_FREQMUL_SHIFT) & SND_RME_CLK_FREQMUL_MASK) #define SND_RME_CLK_AES_LOCK 0x1 #define SND_RME_CLK_AES_SYNC 0x4 #define SND_RME_CLK_SPDIF_LOCK 0x2 @@ -2173,9 +2173,9 @@ static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) #define SND_RME_SPDIF_FORMAT_SHIFT 5 #define SND_RME_BINARY_MASK 0x1 #define SND_RME_SPDIF_IF(x) \ - ((x >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK) + (((x) >> SND_RME_SPDIF_IF_SHIFT) & SND_RME_BINARY_MASK) #define SND_RME_SPDIF_FORMAT(x) \ - ((x >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK) + (((x) >> SND_RME_SPDIF_FORMAT_SHIFT) & SND_RME_BINARY_MASK) static const u32 snd_rme_rate_table[] = { 32000, 44100, 48000, 50000, -- GitLab From 231225d8a20f8668b4fd6601d54a2fac0e0ab7a5 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:43 +0300 Subject: [PATCH 058/868] ALSA: usb-audio: Fix block comments in mixer_quirks Address a couple of comment formatting issues indicated by checkpatch.pl: WARNING: Block comments use a trailing */ on a separate line Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-4-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 19abd709c4d73..5ef8fc714cc32 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -78,7 +78,8 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, cval->idx_off = idx_off; /* get_min_max() is called only for integer volumes later, - * so provide a short-cut for booleans */ + * so provide a short-cut for booleans + */ cval->min = 0; cval->max = 1; cval->res = 0; @@ -4255,7 +4256,8 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, struct snd_kcontrol *kctl) { /* Approximation using 10 ranges based on output measurement on hw v1.2. - * This seems close to the cubic mapping e.g. alsamixer uses. */ + * This seems close to the cubic mapping e.g. alsamixer uses. + */ static const DECLARE_TLV_DB_RANGE(scale, 0, 1, TLV_DB_MINMAX_ITEM(-5300, -4970), 2, 5, TLV_DB_MINMAX_ITEM(-4710, -4160), -- GitLab From c0495cef8b43ad61efbd4019e3573742e0e63c67 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:44 +0300 Subject: [PATCH 059/868] ALSA: usb-audio: Drop unnecessary parentheses in mixer_quirks Fix multiple 'CHECK: Unnecessary parentheses around ...' reports from checkpatch.pl. Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-5-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 5ef8fc714cc32..071a76554d09e 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -380,10 +380,10 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) struct snd_kcontrol_new knew; /* USB X-Fi S51 doesn't have a CMSS LED */ - if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042) && i == 0) continue; /* USB X-Fi S51 Pro doesn't have one either */ - if ((mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) && i == 0) + if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df) && i == 0) continue; if (i > 1 && /* Live24ext has 2 LEDs only */ (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || @@ -3955,7 +3955,7 @@ static int snd_djm_controls_update(struct usb_mixer_interface *mixer, int err; const struct snd_djm_device *device = &snd_djm_devices[device_idx]; - if ((group >= device->ncontrols) || value >= device->controls[group].noptions) + if (group >= device->ncontrols || value >= device->controls[group].noptions) return -EINVAL; err = snd_usb_lock_shutdown(mixer->chip); -- GitLab From 03ddd3bdb94df3edb1f2408b57cfb00b3d92a208 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:45 +0300 Subject: [PATCH 060/868] ALSA: usb-audio: Avoid multiple assignments in mixer_quirks Handle report from checkpatch.pl: CHECK: multiple assignments should be avoided Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-6-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 071a76554d09e..17c9cbbfce224 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1739,7 +1739,8 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, unsigned int pval, pval_old; int err; - pval = pval_old = kcontrol->private_value; + pval = kcontrol->private_value; + pval_old = pval; pval &= 0xfffff0f0; pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; pval |= (ucontrol->value.iec958.status[0] & 0x0f); -- GitLab From f2d6d660e8fd5f4467e80743f82119201e67fa9c Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:46 +0300 Subject: [PATCH 061/868] ALSA: usb-audio: Simplify NULL comparison in mixer_quirks Handle report from checkpatch.pl: CHECK: Comparison to NULL could be written "t->name" Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-7-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 17c9cbbfce224..783789cbcfe29 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -129,7 +129,7 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, { int err; - while (t->name != NULL) { + while (t->name) { err = snd_create_std_mono_ctl(mixer, t->unitid, t->control, t->cmask, t->val_type, t->name, t->tlv_callback); -- GitLab From 9cea7425595697802e8d55a322a251999554b8b1 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:47 +0300 Subject: [PATCH 062/868] ALSA: usb-audio: Remove unneeded wmb() in mixer_quirks Adding a memory barrier before wake_up() in snd_usb_soundblaster_remote_complete() is supposed to ensure the write to mixer->rc_code is visible in wait_event_interruptible() from snd_usb_sbrc_hwdep_read(). However, this is not really necessary, since wake_up() is just a wrapper over __wake_up() which already executes a full memory barrier before accessing the state of the task to be waken up. Drop the redundant call to wmb() and implicitly fix the checkpatch complaint: WARNING: memory barrier without comment Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-8-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 783789cbcfe29..ffda3b8d42cc6 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -212,7 +212,6 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb) if (code == rc->mute_code) snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); mixer->rc_code = code; - wmb(); wake_up(&mixer->rc_waitq); } -- GitLab From 79d561c4ec0497669f19a9550cfb74812f60938b Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 26 May 2025 17:07:48 +0300 Subject: [PATCH 063/868] ALSA: usb-audio: Add mixer quirk for Sony DualSense PS5 The Sony DualSense wireless controller (PS5) features an internal mono speaker, but it also provides a 3.5mm jack socket for headphone output and headset microphone input. Since this is a UAC1 device, it doesn't advertise any jack detection capability. However, the controller is able to report HP & MIC insert events via HID, i.e. through a dedicated input device managed by the hid-playstation driver. Add a quirk to create the jack controls for headphone and headset mic, respectively, and setup an input handler for each of them in order to intercept the related hotplug events. Signed-off-by: Cristian Ciocaltea Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250526-dualsense-alsa-jack-v1-9-1a821463b632@collabora.com --- sound/usb/mixer_quirks.c | 263 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ffda3b8d42cc6..0769ecd911440 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -531,6 +532,263 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) &snd_emu0204_control, NULL); } +/* + * Sony DualSense controller (PS5) jack detection + * + * Since this is an UAC 1 device, it doesn't support jack detection. + * However, the controller hid-playstation driver reports HP & MIC + * insert events through a dedicated input device. + */ + +#define SND_DUALSENSE_JACK_OUT_TERM_ID 3 +#define SND_DUALSENSE_JACK_IN_TERM_ID 4 + +struct dualsense_mixer_elem_info { + struct usb_mixer_elem_info info; + struct input_handler ih; + struct input_device_id id_table[2]; + bool connected; +}; + +static void snd_dualsense_ih_event(struct input_handle *handle, + unsigned int type, unsigned int code, + int value) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_mixer_elem_list *me; + + if (type != EV_SW) + return; + + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); + me = &mei->info.head; + + if ((me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && code == SW_HEADPHONE_INSERT) || + (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && code == SW_MICROPHONE_INSERT)) { + mei->connected = !!value; + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &me->kctl->id); + } +} + +static bool snd_dualsense_ih_match(struct input_handler *handler, + struct input_dev *dev) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_device *snd_dev; + char *input_dev_path, *usb_dev_path; + size_t usb_dev_path_len; + bool match = false; + + mei = container_of(handler, struct dualsense_mixer_elem_info, ih); + snd_dev = mei->info.head.mixer->chip->dev; + + input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); + if (!input_dev_path) { + dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); + return false; + } + + usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); + if (!usb_dev_path) { + dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); + goto free_paths; + } + + /* + * Ensure the VID:PID matched input device supposedly owned by the + * hid-playstation driver belongs to the actual hardware handled by + * the current USB audio device, which implies input_dev_path being + * a subpath of usb_dev_path. + * + * This verification is necessary when there is more than one identical + * controller attached to the host system. + */ + usb_dev_path_len = strlen(usb_dev_path); + if (usb_dev_path_len >= strlen(input_dev_path)) + goto free_paths; + + usb_dev_path[usb_dev_path_len] = '/'; + match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); + +free_paths: + kfree(input_dev_path); + kfree(usb_dev_path); + + return match; +} + +static int snd_dualsense_ih_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int err; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = handler->name; + + err = input_register_handle(handle); + if (err) + goto err_free; + + err = input_open_device(handle); + if (err) + goto err_unregister; + + return 0; + +err_unregister: + input_unregister_handle(handle); +err_free: + kfree(handle); + return err; +} + +static void snd_dualsense_ih_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static void snd_dualsense_ih_start(struct input_handle *handle) +{ + struct dualsense_mixer_elem_info *mei; + struct usb_mixer_elem_list *me; + int status = -1; + + mei = container_of(handle->handler, struct dualsense_mixer_elem_info, ih); + me = &mei->info.head; + + if (me->id == SND_DUALSENSE_JACK_OUT_TERM_ID && + test_bit(SW_HEADPHONE_INSERT, handle->dev->swbit)) + status = test_bit(SW_HEADPHONE_INSERT, handle->dev->sw); + else if (me->id == SND_DUALSENSE_JACK_IN_TERM_ID && + test_bit(SW_MICROPHONE_INSERT, handle->dev->swbit)) + status = test_bit(SW_MICROPHONE_INSERT, handle->dev->sw); + + if (status >= 0) { + mei->connected = !!status; + snd_ctl_notify(me->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &me->kctl->id); + } +} + +static int snd_dualsense_jack_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); + + ucontrol->value.integer.value[0] = mei->connected; + + return 0; +} + +static const struct snd_kcontrol_new snd_dualsense_jack_control = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = snd_dualsense_jack_get, +}; + +static int snd_dualsense_resume_jack(struct usb_mixer_elem_list *list) +{ + snd_ctl_notify(list->mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &list->kctl->id); + return 0; +} + +static void snd_dualsense_mixer_elem_free(struct snd_kcontrol *kctl) +{ + struct dualsense_mixer_elem_info *mei = snd_kcontrol_chip(kctl); + + if (mei->ih.event) + input_unregister_handler(&mei->ih); + + snd_usb_mixer_elem_free(kctl); +} + +static int snd_dualsense_jack_create(struct usb_mixer_interface *mixer, + const char *name, bool is_output) +{ + struct dualsense_mixer_elem_info *mei; + struct input_device_id *idev_id; + struct snd_kcontrol *kctl; + int err; + + mei = kzalloc(sizeof(*mei), GFP_KERNEL); + if (!mei) + return -ENOMEM; + + snd_usb_mixer_elem_init_std(&mei->info.head, mixer, + is_output ? SND_DUALSENSE_JACK_OUT_TERM_ID : + SND_DUALSENSE_JACK_IN_TERM_ID); + + mei->info.head.resume = snd_dualsense_resume_jack; + mei->info.val_type = USB_MIXER_BOOLEAN; + mei->info.channels = 1; + mei->info.min = 0; + mei->info.max = 1; + + kctl = snd_ctl_new1(&snd_dualsense_jack_control, mei); + if (!kctl) { + kfree(mei); + return -ENOMEM; + } + + strscpy(kctl->id.name, name, sizeof(kctl->id.name)); + kctl->private_free = snd_dualsense_mixer_elem_free; + + err = snd_usb_mixer_add_control(&mei->info.head, kctl); + if (err) + return err; + + idev_id = &mei->id_table[0]; + idev_id->flags = INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT | + INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT; + idev_id->vendor = USB_ID_VENDOR(mixer->chip->usb_id); + idev_id->product = USB_ID_PRODUCT(mixer->chip->usb_id); + idev_id->evbit[BIT_WORD(EV_SW)] = BIT_MASK(EV_SW); + if (is_output) + idev_id->swbit[BIT_WORD(SW_HEADPHONE_INSERT)] = BIT_MASK(SW_HEADPHONE_INSERT); + else + idev_id->swbit[BIT_WORD(SW_MICROPHONE_INSERT)] = BIT_MASK(SW_MICROPHONE_INSERT); + + mei->ih.event = snd_dualsense_ih_event; + mei->ih.match = snd_dualsense_ih_match; + mei->ih.connect = snd_dualsense_ih_connect, + mei->ih.disconnect = snd_dualsense_ih_disconnect, + mei->ih.start = snd_dualsense_ih_start, + mei->ih.name = name; + mei->ih.id_table = mei->id_table; + + err = input_register_handler(&mei->ih); + if (err) { + dev_warn(&mixer->chip->dev->dev, + "Could not register input handler: %d\n", err); + mei->ih.event = NULL; + } + + return 0; +} + +static int snd_dualsense_controls_create(struct usb_mixer_interface *mixer) +{ + int err; + + err = snd_dualsense_jack_create(mixer, "Headphone Jack", true); + if (err < 0) + return err; + + return snd_dualsense_jack_create(mixer, "Headset Mic Jack", false); +} + /* ASUS Xonar U1 / U3 controls */ static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, @@ -4073,6 +4331,11 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_emu0204_controls_create(mixer); break; + case USB_ID(0x054c, 0x0ce6): /* Sony DualSense controller (PS5) */ + case USB_ID(0x054c, 0x0df2): /* Sony DualSense Edge controller (PS5) */ + err = snd_dualsense_controls_create(mixer); + break; + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */ err = snd_c400_create_mixer(mixer); -- GitLab From 9955ea2e3698411fc798b2056facdf3f6e56aa61 Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Mon, 2 Jun 2025 09:58:09 +0800 Subject: [PATCH 064/868] ALSA: hda/tas2781: Add missed brace and hardware id re-order This patch is do code clean and prepare for next patch, which is relative with hardware id. Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20250602015809.20344-1-baojun.xu@ti.com Signed-off-by: Takashi Iwai --- sound/pci/hda/tas2781_hda_i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index d91eed9f78043..eb2295b490d94 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -591,8 +591,9 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) device_name = "INT8866"; hda_priv->save_calibration = tas2563_save_calibration; tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR; - } else + } else { return -ENODEV; + } tas_hda->priv->irq = clt->irq; ret = tas2781_read_acpi(tas_hda->priv, device_name); @@ -722,8 +723,8 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = { }; static const struct acpi_device_id tas2781_acpi_hda_match[] = { - {"TIAS2781", 0 }, {"INT8866", 0 }, + {"TIAS2781", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); -- GitLab From ed57a3d5a3118b9681de607692608fe573c5959d Mon Sep 17 00:00:00 2001 From: Lucy Thrun Date: Tue, 3 Jun 2025 14:53:04 +0200 Subject: [PATCH 065/868] ALSA: hda/ca0132: Enable hardware band EQ for Sound Blaster Core3D This patch enables the Hardware DSP-based 10-band hardware equalizer on Sound Blaster Core3D (CA0132) based cards for example the soundblaster z and soundblaster x-ae5, allowing fine-grained audio tuning via hardware registers. Tested on Sound Blaster X-AE5. No regressions observed. Signed-off-by: Lucy Thrun Link: https://patch.msgid.link/20250603125304.507-1-lucy.thrun@digital-rabbithole.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index d40197fb5fbd5..cfe422a797033 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -28,7 +28,7 @@ #include "ca0132_regs.h" /* Enable this to see controls for tuning purpose. */ -/*#define ENABLE_TUNING_CONTROLS*/ +#define ENABLE_TUNING_CONTROLS #ifdef ENABLE_TUNING_CONTROLS #include -- GitLab From 92f59aeb13252265c20e7aef1379a8080c57e0a2 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 6 Jun 2025 11:44:02 +0200 Subject: [PATCH 066/868] ALSA: pcm: Rewrite recalculate_boundary() to avoid costly loop At the time being recalculate_boundary() is implemented with a loop which shows up as costly in a perf profile, as depicted by the annotate below: 0.00 : c057e934: 3d 40 7f ff lis r10,32767 0.03 : c057e938: 61 4a ff ff ori r10,r10,65535 0.21 : c057e93c: 7d 49 50 50 subf r10,r9,r10 5.39 : c057e940: 7d 3c 4b 78 mr r28,r9 2.11 : c057e944: 55 29 08 3c slwi r9,r9,1 3.04 : c057e948: 7c 09 50 40 cmplw r9,r10 2.47 : c057e94c: 40 81 ff f4 ble c057e940 Total: 13.2% on that simple loop. But what the loop does is to multiply the boundary by 2 until it is over the wanted border. This can be avoided by using fls() to get the boundary value order and shift it by the appropriate number of bits at once. This change provides the following profile: 0.04 : c057f6e8: 3d 20 7f ff lis r9,32767 0.02 : c057f6ec: 61 29 ff ff ori r9,r9,65535 0.34 : c057f6f0: 7d 5a 48 50 subf r10,r26,r9 0.23 : c057f6f4: 7c 1a 50 40 cmplw r26,r10 0.02 : c057f6f8: 41 81 00 20 bgt c057f718 0.26 : c057f6fc: 7f 47 00 34 cntlzw r7,r26 0.09 : c057f700: 7d 48 00 34 cntlzw r8,r10 0.22 : c057f704: 7d 08 38 50 subf r8,r8,r7 0.04 : c057f708: 7f 5a 40 30 slw r26,r26,r8 0.35 : c057f70c: 7c 0a d0 40 cmplw r10,r26 0.13 : c057f710: 40 80 05 f8 bge c057fd08 0.00 : c057f714: 57 5a f8 7e srwi r26,r26,1 Total: 1.7% with that loopless alternative. Signed-off-by: Christophe Leroy Link: https://patch.msgid.link/4836e2cde653eebaf2709ebe30eec736bb8c67fd.1749202237.git.christophe.leroy@csgroup.eu Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 853ac5bb33ff2..ecb71bf1859d4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "pcm_local.h" @@ -3130,13 +3131,23 @@ struct snd_pcm_sync_ptr32 { static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime) { snd_pcm_uframes_t boundary; + snd_pcm_uframes_t border; + int order; if (! runtime->buffer_size) return 0; - boundary = runtime->buffer_size; - while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) - boundary *= 2; - return boundary; + + border = 0x7fffffffUL - runtime->buffer_size; + if (runtime->buffer_size > border) + return runtime->buffer_size; + + order = __fls(border) - __fls(runtime->buffer_size); + boundary = runtime->buffer_size << order; + + if (boundary <= border) + return boundary; + else + return boundary / 2; } static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, -- GitLab From 844d8e4c7f9a3eeb681493f12c55de0392510fe3 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Wed, 28 May 2025 07:47:22 -0300 Subject: [PATCH 067/868] platform/x86: alienware-wmi-wmax: Add appropriate labels to fans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add known fan type IDs and match them to an appropriate label in awcc_hwmon_read_string(). Additionally, add the AWCC_TEMP_SENSOR_FRONT type, which was inferred from it's related fan type in supported systems. Signed-off-by: Kurt Borja Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20250528-awcc-labels-v1-1-6aa39d8e4c3d@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/dell/alienware-wmi-wmax.c | 100 +++++++++++------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c index c42f9228b0b25..b25eb3225d8e5 100644 --- a/drivers/platform/x86/dell/alienware-wmi-wmax.c +++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c @@ -273,9 +273,29 @@ enum AWCC_SPECIAL_THERMAL_CODES { enum AWCC_TEMP_SENSOR_TYPES { AWCC_TEMP_SENSOR_CPU = 0x01, + AWCC_TEMP_SENSOR_FRONT = 0x03, AWCC_TEMP_SENSOR_GPU = 0x06, }; +enum AWCC_FAN_TYPES { + AWCC_FAN_CPU_1 = 0x32, + AWCC_FAN_GPU_1 = 0x33, + AWCC_FAN_PCI = 0x34, + AWCC_FAN_MID = 0x35, + AWCC_FAN_TOP_1 = 0x36, + AWCC_FAN_SIDE = 0x37, + AWCC_FAN_U2_1 = 0x38, + AWCC_FAN_U2_2 = 0x39, + AWCC_FAN_FRONT_1 = 0x3A, + AWCC_FAN_CPU_2 = 0x3B, + AWCC_FAN_GPU_2 = 0x3C, + AWCC_FAN_TOP_2 = 0x3D, + AWCC_FAN_TOP_3 = 0x3E, + AWCC_FAN_FRONT_2 = 0x3F, + AWCC_FAN_BOTTOM_1 = 0x40, + AWCC_FAN_BOTTOM_2 = 0x41, +}; + enum awcc_thermal_profile { AWCC_PROFILE_USTT_BALANCED, AWCC_PROFILE_USTT_BALANCED_PERFORMANCE, @@ -314,7 +334,6 @@ struct wmax_u32_args { struct awcc_fan_data { unsigned long auto_channels_temp; - const char *label; u32 min_rpm; u32 max_rpm; u8 suspend_cache; @@ -896,6 +915,9 @@ static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types ty case AWCC_TEMP_SENSOR_CPU: *str = "CPU"; break; + case AWCC_TEMP_SENSOR_FRONT: + *str = "Front"; + break; case AWCC_TEMP_SENSOR_GPU: *str = "GPU"; break; @@ -906,7 +928,46 @@ static int awcc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types ty break; case hwmon_fan: - *str = priv->fan_data[channel]->label; + switch (priv->fan_data[channel]->id) { + case AWCC_FAN_CPU_1: + case AWCC_FAN_CPU_2: + *str = "CPU Fan"; + break; + case AWCC_FAN_GPU_1: + case AWCC_FAN_GPU_2: + *str = "GPU Fan"; + break; + case AWCC_FAN_PCI: + *str = "PCI Fan"; + break; + case AWCC_FAN_MID: + *str = "Mid Fan"; + break; + case AWCC_FAN_TOP_1: + case AWCC_FAN_TOP_2: + case AWCC_FAN_TOP_3: + *str = "Top Fan"; + break; + case AWCC_FAN_SIDE: + *str = "Side Fan"; + break; + case AWCC_FAN_U2_1: + case AWCC_FAN_U2_2: + *str = "U.2 Fan"; + break; + case AWCC_FAN_FRONT_1: + case AWCC_FAN_FRONT_2: + *str = "Front Fan"; + break; + case AWCC_FAN_BOTTOM_1: + case AWCC_FAN_BOTTOM_2: + *str = "Bottom Fan"; + break; + default: + *str = "Unknown Fan"; + break; + } + break; default: return -EOPNOTSUPP; @@ -1051,40 +1112,6 @@ static int awcc_hwmon_temps_init(struct wmi_device *wdev) return 0; } -static char *awcc_get_fan_label(unsigned long *fan_temps) -{ - unsigned int temp_count = bitmap_weight(fan_temps, AWCC_ID_BITMAP_SIZE); - char *label; - u8 temp_id; - - switch (temp_count) { - case 0: - label = "Independent Fan"; - break; - case 1: - temp_id = find_first_bit(fan_temps, AWCC_ID_BITMAP_SIZE); - - switch (temp_id) { - case AWCC_TEMP_SENSOR_CPU: - label = "Processor Fan"; - break; - case AWCC_TEMP_SENSOR_GPU: - label = "Video Fan"; - break; - default: - label = "Unknown Fan"; - break; - } - - break; - default: - label = "Shared Fan"; - break; - } - - return label; -} - static int awcc_hwmon_fans_init(struct wmi_device *wdev) { struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); @@ -1138,7 +1165,6 @@ static int awcc_hwmon_fans_init(struct wmi_device *wdev) fan_data->id = id; fan_data->min_rpm = min_rpm; fan_data->max_rpm = max_rpm; - fan_data->label = awcc_get_fan_label(fan_temps); bitmap_gather(gather, fan_temps, priv->temp_sensors, AWCC_ID_BITMAP_SIZE); bitmap_copy(&fan_data->auto_channels_temp, gather, BITS_PER_LONG); priv->fan_data[i] = fan_data; -- GitLab From e7c1a9e8d33ceb44ef088de7a9112a1db94d13a4 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 3 Jun 2025 05:58:07 +0000 Subject: [PATCH 068/868] platform/x86/amd/hsmp: Use IS_ENABLED() instead of IS_REACHABLE() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IS_REACHABLE() was required when CONFIG_HWMON was set to m and HSMP to y. However, commit 69157b00b526 ("platform/x86/amd/hsmp: fix building with CONFIG_HWMON=m") added a HWMON dependency for HSMP in Kconfig. With this change, using IS_ENABLED() is sufficient. Add the missing header file as well. Signed-off-by: Suma Hegde Link: https://lore.kernel.org/r/20250603055807.2503028-1-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp/hsmp.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index 36b5ceea9ac04..0509a442eaae6 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ int hsmp_misc_register(struct device *dev); int hsmp_get_tbl_dram_base(u16 sock_ind); ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size); struct hsmp_plat_device *get_hsmp_pdev(void); -#if IS_REACHABLE(CONFIG_HWMON) +#if IS_ENABLED(CONFIG_HWMON) int hsmp_create_sensor(struct device *dev, u16 sock_ind); #else static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; } -- GitLab From 5c694e3a83d089df6b00747cf4627735ea14014e Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Mon, 9 Jun 2025 15:47:27 +0800 Subject: [PATCH 069/868] ASoC: dt-bindings: rt9123: Append RTQ9124 description Document the ASoC Richtek RTQ9124 in existed RT9123 file. Signed-off-by: ChiYuan Huang Link: https://patch.msgid.link/9921d64ce4d63c24499f92ef33a4ce7cb018c60b.1749454717.git.cy_huang@richtek.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/richtek,rt9123.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml b/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml index 5acb05cdfefd7..819ca06203b18 100644 --- a/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml +++ b/Documentation/devicetree/bindings/sound/richtek,rt9123.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/sound/richtek,rt9123.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Richtek RT9123 Audio Amplifier +title: Richtek RT9123/RTQ9124 Audio Amplifier maintainers: - ChiYuan Huang @@ -15,6 +15,12 @@ description: support various formats, including I2S, left-justified, right-justified, and TDM formats. + RTQ9124 is an ultra-low output noise, digital input, mono-channel Class-D + power amplifier that supports a 2.1MHz switching frequency. It integrates + both DC and AC load diagnostics, as well as real-time load monitoring to + assess speaker condition. The device operates from 4.5V to 18V and delivers + up to 30W output power. + allOf: - $ref: dai-common.yaml# @@ -22,6 +28,7 @@ properties: compatible: enum: - richtek,rt9123 + - richtek,rtq9124 reg: maxItems: 1 -- GitLab From 1f5cdb6ab45e1c06ae0953609acbb52f8946b3e8 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Mon, 9 Jun 2025 15:47:28 +0800 Subject: [PATCH 070/868] ASoC: codecs: Add support for Richtek RTQ9124 Add codecs driver for Richtek RTQ9124. Signed-off-by: ChiYuan Huang Link: https://patch.msgid.link/aca49d1912bfd1b90e82146df6393760a731810c.1749454717.git.cy_huang@richtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 4 +- sound/soc/codecs/rtq9124.c | 543 +++++++++++++++++++++++++++++++++++++ 3 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/rtq9124.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 126f897312d47..d0c01f49a8ba3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -238,6 +238,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 imply SND_SOC_RT9123 + imply SND_SOC_RTQ9124 imply SND_SOC_RTQ9128 imply SND_SOC_SDW_MOCKUP imply SND_SOC_SGTL5000 @@ -1858,6 +1859,14 @@ config SND_SOC_RT9123P Enable support for the HW control mode of Richtek RT9123P 3.2W mono Class-D audio amplifier. +config SND_SOC_RTQ9124 + tristate "Richtek RTQ9124 Mono Class-D Amplifier" + depends on I2C + select REGMAP + help + Enable support for Richtek RTQ9124 1x30W digital input automotive + audio amplifier with current sense and real-time load diagnostics. + config SND_SOC_RTQ9128 tristate "Richtek RTQ9128 45W Digital Input Amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6d7aa109ede7c..a68c3d192a1b6 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -275,6 +275,7 @@ snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-y := rt9120.o snd-soc-rt9123-y := rt9123.o snd-soc-rt9123p-y := rt9123p.o +snd-soc-rtq9124-y := rtq9124.o snd-soc-rtq9128-y := rtq9128.o snd-soc-sdw-mockup-y := sdw-mockup.o snd-soc-sgtl5000-y := sgtl5000.o @@ -695,6 +696,7 @@ obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o +obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -853,4 +855,4 @@ obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o # Mux -obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o \ No newline at end of file +obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c new file mode 100644 index 0000000000000..186904b31434c --- /dev/null +++ b/sound/soc/codecs/rtq9124.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rtq9124.c -- RTQ9124 ALSA SoC Codec driver +// +// Author: ChiYuan Huang + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTQ9124_REG_SDI_SEL 0x00 +#define RTQ9124_REG_SDO_SEL 0x01 +#define RTQ9124_REG_I2S_OPT 0x02 +#define RTQ9124_REG_AMP_OPT 0x03 +#define RTQ9124_REG_STATE_CTRL 0x04 +#define RTQ9124_REG_PWM_PHASE 0x05 +#define RTQ9124_REG_SIL_CTRL 0x06 +#define RTQ9124_REG_PWM_SS_OPT 0x07 +#define RTQ9124_REG_ERR_INT_0 0x10 +#define RTQ9124_REG_ERR_MASK6 0x26 +#define RTQ9124_REG_TDM_TX_CH0 0x32 +#define RTQ9124_REG_TDM_RX_CH0 0x34 +#define RTQ9124_REG_VOL_OPT 0x38 +#define RTQ9124_REG_DCR_TH 0x4B +#define RTQ9124_REG_ERR_TH 0x4C +#define RTQ9124_REG_PROT_EN 0x5B +#define RTQ9124_REG_PRJ_CODE 0xF9 + +#define RTQ9124_MASK_CS_DATA_INV BIT(9) +#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8) +#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4) +#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0) +#define RTQ9124_MASK_CH_STATE GENMASK(1, 0) +#define RTQ9124_MASK_SF_RESET BIT(15) + +#define RTQ9124_FIXED_VENID 0x9124 + +struct rtq9124_priv { + struct gpio_desc *enable; + unsigned int dai_fmt; + int tdm_slots; + int tdm_slot_width; +}; + +static int rtq9124_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int i, chan_state; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Change state to normal */ + chan_state = 0; + break; + case SND_SOC_DAPM_POST_PMD: + /* Change state to HiZ */ + chan_state = 1; + break; + default: + return -EINVAL; + } + + /* Before amp turn on, clear old events first */ + for (i = 0; !chan_state && i < 8; i++) + snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff); + + snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE, + chan_state); + + return 0; +} + +static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = { + { "Amp Drv", NULL, "HiFi Playback" }, + { "SPK", NULL, "Amp Drv" }, +}; + +static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0); +static const DECLARE_TLV_DB_RANGE(ana_tlv, + 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0), + 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0)); +static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" }; +static const struct soc_enum rtq9124_i2sch_select_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text); +static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" }; +static const struct soc_enum rtq9124_sdo_vselect_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text); +static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" }; +static const struct soc_enum rtq9124_pwm_freq_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text); +static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" }; +static const struct soc_enum rtq9124_out_angle_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text); +static const char * const sdo_select_text[] = { + "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin", + "DCR", +}; +static const struct soc_enum rtq9124_sdo_select_enum = + SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text); +static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" }; +static const struct soc_enum rtq9124_ulqm_dcvt_select_enum = + SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text); + +static const struct snd_kcontrol_new rtq9124_controls[] = { + SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv), + SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv), + SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum), + SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum), + SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum), + SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum), + SOC_ENUM("SDO Select", rtq9124_sdo_select_enum), + SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum), + SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0), + SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0), +}; + +static int rtq9124_comp_probe(struct snd_soc_component *comp) +{ + /* CS Data INV */ + snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1); + + /* RTLD */ + snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30); + snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff); + snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc); + snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0); + + return 0; +} + +static const struct snd_soc_component_driver rtq9124_comp_driver = { + .probe = rtq9124_comp_probe, + .controls = rtq9124_controls, + .num_controls = ARRAY_SIZE(rtq9124_controls), + .dapm_widgets = rtq9124_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets), + .dapm_routes = rtq9124_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + + rtq9124->dai_fmt = fmt; + return 0; +} + +static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + struct device *dev = dai->dev; + unsigned int byte_loc, i; + + dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots, + slot_width, tx_mask, rx_mask); + + if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) { + dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width); + return -EINVAL; + } + + if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) { + dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots); + return -EINVAL; + } + + if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) { + dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots); + return -EINVAL; + } + + /* Configure tx channel data location */ + for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) { + byte_loc = (ffs(tx_mask) - 1) * slot_width / 8; + snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc); + } + + /* Configure rx channel data location */ + byte_loc = (ffs(rx_mask) - 1) * slot_width / 8; + snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc); + + rtq9124->tdm_slots = slots; + rtq9124->tdm_slot_width = slot_width; + + return 0; +} + +static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *param, struct snd_soc_dai *dai) +{ + struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *comp = dai->component; + unsigned int fmtval, width, slot_width, bitrate; + struct device *dev = dai->dev; + unsigned int audfmt, audbit; + + fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt); + if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && + fmtval != SND_SOC_DAIFMT_DSP_B) { + dev_err(dev, "TDM only can support DSP_A or DSP_B format\n"); + return -EINVAL; + } + + switch (fmtval) { + case SND_SOC_DAIFMT_I2S: + audfmt = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + audfmt = 1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audfmt = 2; + break; + case SND_SOC_DAIFMT_DSP_B: + audfmt = rtq9124->tdm_slots ? 7 : 3; + break; + case SND_SOC_DAIFMT_DSP_A: + audfmt = rtq9124->tdm_slots ? 15 : 11; + break; + default: + dev_err(dev, "Unsupported format %d\n", fmtval); + return -EINVAL; + } + + switch (width = params_width(param)) { + case 16: + audbit = 0; + break; + case 20: + audbit = 1; + break; + case 24: + case 32: + audbit = 3; + break; + default: + dev_err(dev, "Unsupported width %d\n", width); + return -EINVAL; + } + + if (rtq9124->tdm_slots) { + slot_width = params_physical_width(param); + if (slot_width > rtq9124->tdm_slot_width) { + dev_err(dev, "Slot width is larger than TDM slot width\n"); + return -EINVAL; + } + + bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param); + if (bitrate > 24576000) { + dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate); + return -EINVAL; + } + } + + snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt); + snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit); + + return 0; +} + +static const struct snd_soc_dai_ops rtq9124_dai_ops = { + .set_fmt = rtq9124_dai_set_format, + .set_tdm_slot = rtq9124_dai_set_tdm_slot, + .hw_params = rtq9124_dai_hw_params, +}; + +static struct snd_soc_dai_driver rtq9124_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rtq9124_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static bool rtq9124_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x17: + case 0x20 ... 0x27: + case 0x30 ... 0x3D: + case 0x40 ... 0x68: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x09: + case 0x0C ... 0x0E: + case 0x10 ... 0x17: + case 0x20 ... 0x27: + case 0x30: + case 0x32 ... 0x3D: + case 0x40 ... 0x4E: + case 0x50 ... 0x68: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0A ... 0x0B: + case 0x0F ... 0x17: + case 0x31: + case 0x4F: + case 0x51: + case 0x53 ... 0x57: + case 0x80 ... 0xBC: + case 0xC0 ... 0xDE: + case 0xE0 ... 0xE7: + case 0xF0 ... 0xFD: + return true; + default: + return false; + } +} + +static inline u8 rtq9124_get_reg_len(unsigned int reg) +{ + return (reg >= 0x40 && reg <= 0x47) ? 4 : 2; +} + +static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf, + size_t val_size) +{ + struct i2c_client *i2c = context; + u8 reg = *(u8 *)reg_buf; + u8 size = rtq9124_get_reg_len(reg); + u32 *val = val_buf; + int ret; + + ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf); + if (ret < 0) + return ret; + else if (ret != size) + return -EIO; + + *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf); + + return 0; +} + +static int rtq9124_regmap_write(void *context, const void *data, size_t count) +{ + struct i2c_client *i2c = context; + u8 reg = *(u8 *)data, *vbuf; + u8 size = rtq9124_get_reg_len(reg); + __be16 val16 = cpu_to_be16p(data + 1); + __be32 val32 = cpu_to_be32p(data + 1); + + vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16; + return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf); +} + +static const struct regmap_config rtq9124_regmap_config = { + .name = "rtq9124", + .reg_bits = 8, + .val_bits = 32, + .read = rtq9124_regmap_read, + .write = rtq9124_regmap_write, + .readable_reg = rtq9124_readable_reg, + .writeable_reg = rtq9124_writeable_reg, + .volatile_reg = rtq9124_volatile_reg, + .cache_type = REGCACHE_MAPLE, + .num_reg_defaults_raw = 0xFD + 1, + .use_single_read = 1, + .use_single_write = 1, +}; + +static const struct reg_sequence rtq9124_init_regs[] = { + { 0xfb, 0x0065 }, + { 0x93, 0x2000 }, + { 0xfb, 0x0000 }, +}; + +static int rtq9124_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rtq9124_priv *rtq9124; + struct regmap *regmap; + int ret; + + rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL); + if (!rtq9124) + return -ENOMEM; + + rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(rtq9124->enable)) + return PTR_ERR(rtq9124->enable); + else if (rtq9124->enable) + usleep_range(6000, 7000); + else + dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n"); + + /* Check vendor id information */ + ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read project code\n"); + else if (ret != RTQ9124_FIXED_VENID) + return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret); + + /* Trigger RG reset before regmap init */ + ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET); + if (ret) + return dev_err_probe(dev, ret, "Failed to trigger RG reset\n"); + + /* Need to wait 10ms for the reset to complete */ + usleep_range(10000, 11000); + + regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); + + ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs)); + if (ret) + return dev_err_probe(dev, ret, "Failed to register regmap patch\n"); + + i2c_set_clientdata(i2c, rtq9124); + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n"); + + return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1); +} + +#ifdef CONFIG_PM +static int rtq9124_runtime_suspend(struct device *dev) +{ + struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + + if (rtq9124->enable) { + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + gpiod_set_value(rtq9124->enable, 0); + } + + return 0; +} + +static int rtq9124_runtime_resume(struct device *dev) +{ + struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + int ret; + + if (rtq9124->enable) { + gpiod_set_value(rtq9124->enable, 1); + usleep_range(6000, 7000); + + regcache_cache_only(regmap, false); + ret = regcache_sync(regmap); + if (ret) + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops rtq9124_dev_pm_ops = { + SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id rtq9124_device_id[] = { + { .compatible = "richtek,rtq9124" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtq9124_device_id); +#endif + +static struct i2c_driver rtq9124_driver = { + .driver = { + .name = "rtq9124", + .of_match_table = of_match_ptr(rtq9124_device_id), + .pm = pm_ptr(&rtq9124_dev_pm_ops), + }, + .probe = rtq9124_probe, +}; +module_i2c_driver(rtq9124_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("ASoC RTQ9124 Driver"); +MODULE_LICENSE("GPL"); -- GitLab From 6d09c6e474bd27a86352deaf73d02c8c21eeec7c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Jun 2025 02:06:41 +0200 Subject: [PATCH 071/868] regulator: dt-bindings: rpi-panel: Add regulator for 7" Raspberry Pi 720x1280 Document regulator compatible string for the 7" Raspberry Pi 720x1280 DSI panel based on ili9881. This is the Raspberry Pi DSI Panel V2. The newer Raspberry Pi 5" and 7" panels have a slightly different register map to the original one and offers PWM backlight control. Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609000748.1665219-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- .../raspberrypi,7inch-touchscreen-panel-regulator.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml index 41678400e63fa..18944d39d08fc 100644 --- a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml @@ -12,14 +12,17 @@ maintainers: description: | The RaspberryPi 7" display has an ATTINY88-based regulator/backlight controller on the PCB, which is used to turn the display unit on/off - and control the backlight. + and control the backlight. The V2 supports 5" and 7" panels and also + offers PWM backlight control. allOf: - $ref: regulator.yaml# properties: compatible: - const: raspberrypi,7inch-touchscreen-panel-regulator + enum: + - raspberrypi,7inch-touchscreen-panel-regulator + - raspberrypi,touchscreen-panel-regulator-v2 reg: maxItems: 1 -- GitLab From d49305862fdc4d9ff1b1093b4ed7d8e0cb9971b4 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 9 Jun 2025 02:06:42 +0200 Subject: [PATCH 072/868] regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280 Add regulator for the 7" Raspberry Pi 720x1280 DSI panel based on ili9881. This is the Raspberry Pi DSI Panel V2. The newer Raspberry Pi 5" and 7" panels have a slightly different register map to the original one. Add a new driver for this "regulator" chip, this time by exposing two GPIOs and one PWM controller, both of which can be consumed by panel driver and pwm-backlight respectively. Signed-off-by: Dave Stevenson Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609000748.1665219-2-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 1 + drivers/regulator/rpi-panel-v2-regulator.c | 114 +++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 drivers/regulator/rpi-panel-v2-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6d8988387da45..21ad6d938e4d4 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1153,6 +1153,16 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY touchscreen unit. The regulator is used to enable power to the TC358762, display and to control backlight. +config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 + tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" + depends on I2C + select GPIO_REGMAP + select REGMAP_I2C + help + This driver supports regulator on the V2 Raspberry Pi touchscreen + unit. The regulator is used to enable power to the display and to + control backlight PWM. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c0bc7a0f4e670..be98b29d6675d 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o +obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2) += rpi-panel-v2-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c new file mode 100644 index 0000000000000..b77383584a3a6 --- /dev/null +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Raspberry Pi Ltd. + * Copyright (C) 2025 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include +#include + +/* I2C registers of the microcontroller. */ +#define REG_ID 0x01 +#define REG_POWERON 0x02 +#define REG_PWM 0x03 + +/* Bits for poweron register */ +#define LCD_RESET_BIT BIT(0) +#define CTP_RESET_BIT BIT(1) + +/* Bits for the PWM register */ +#define PWM_BL_ENABLE BIT(7) +#define PWM_BL_MASK GENMASK(4, 0) + +/* Treat LCD_RESET and CTP_RESET as GPIOs */ +#define NUM_GPIO 2 + +static const struct regmap_config rpi_panel_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_PWM, + .can_sleep = true, +}; + +static int rpi_panel_v2_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct regmap *regmap = pwmchip_get_drvdata(chip); + unsigned int duty; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) + return regmap_write(regmap, REG_PWM, 0); + + duty = pwm_get_relative_duty_cycle(state, PWM_BL_MASK); + return regmap_write(regmap, REG_PWM, duty | PWM_BL_ENABLE); +} + +static const struct pwm_ops rpi_panel_v2_pwm_ops = { + .apply = rpi_panel_v2_pwm_apply, +}; + +/* + * I2C driver interface functions + */ +static int rpi_panel_v2_i2c_probe(struct i2c_client *i2c) +{ + struct gpio_regmap_config gconfig = { + .ngpio = NUM_GPIO, + .ngpio_per_reg = NUM_GPIO, + .parent = &i2c->dev, + .reg_set_base = REG_POWERON, + }; + struct regmap *regmap; + struct pwm_chip *pc; + int ret; + + pc = devm_pwmchip_alloc(&i2c->dev, 1, 0); + if (IS_ERR(pc)) + return PTR_ERR(pc); + + pc->ops = &rpi_panel_v2_pwm_ops; + + regmap = devm_regmap_init_i2c(i2c, &rpi_panel_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), "Failed to allocate regmap\n"); + + pwmchip_set_drvdata(pc, regmap); + + regmap_write(regmap, REG_POWERON, 0); + + gconfig.regmap = regmap; + ret = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&i2c->dev, &gconfig)); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to create gpiochip\n"); + + return devm_pwmchip_add(&i2c->dev, pc); +} + +static const struct of_device_id rpi_panel_v2_dt_ids[] = { + { .compatible = "raspberrypi,touchscreen-panel-regulator-v2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rpi_panel_v2_dt_ids); + +static struct i2c_driver rpi_panel_v2_regulator_driver = { + .driver = { + .name = "rpi_touchscreen_v2", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(rpi_panel_v2_dt_ids), + }, + .probe = rpi_panel_v2_i2c_probe, +}; + +module_i2c_driver(rpi_panel_v2_regulator_driver); + +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch V2 touchscreen"); +MODULE_LICENSE("GPL"); -- GitLab From 0779c0ad2a7cc0ae1865860c9bc8732613cc56b1 Mon Sep 17 00:00:00 2001 From: Venkata Prasad Potturu Date: Mon, 9 Jun 2025 17:42:32 +0530 Subject: [PATCH 073/868] ASoC: amd: acp: Fix pointer assignments for snd_soc_acpi_mach structures This patch modifies the assignment of machine structure pointers in the acp_pci_probe function. Previously, the machine pointers were assigned using the address-of operator (&), which caused incompatibility issues in type assignments. Additionally, the declarations of the machine arrays in amd.h have been updated to reflect that they are indeed arrays (`[]`). The code is further cleaned up by declaring the codec structures in amd-acpi-mach.c as static, reflecting their intended usage. error: symbol 'amp_rt1019' was not declared. Should it be static? error: symbol 'amp_max' was not declared. Should it be static? error: symbol 'snd_soc_acpi_amd_acp_machines' was not declared. Should it be static? error: symbol 'snd_soc_acpi_amd_rmb_acp_machines' was not declared. Should it be static? error: symbol 'snd_soc_acpi_amd_acp63_acp_machines' was not declared. Should it be static? error: symbol 'snd_soc_acpi_amd_acp70_acp_machines' was not declared. Should it be static? Fixes: 9c2c0ef64009 ("ASoC: amd: acp: Fix snd_soc_acpi_mach id's duplicate symbol error") Link: https://github.com/thesofproject/linux/issues/5438 Signed-off-by: Venkata Prasad Potturu Link: https://patch.msgid.link/20250609121251.639080-1-venkataprasad.potturu@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-pci.c | 8 ++++---- sound/soc/amd/acp/amd-acpi-mach.c | 4 ++-- sound/soc/amd/acp/amd.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 0b2aa33cc426f..2591b1a1c5e00 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -137,26 +137,26 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id chip->name = "acp_asoc_renoir"; chip->rsrc = &rn_rsrc; chip->acp_hw_ops_init = acp31_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp_machines; + chip->machines = snd_soc_acpi_amd_acp_machines; break; case 0x6f: chip->name = "acp_asoc_rembrandt"; chip->rsrc = &rmb_rsrc; chip->acp_hw_ops_init = acp6x_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_rmb_acp_machines; + chip->machines = snd_soc_acpi_amd_rmb_acp_machines; break; case 0x63: chip->name = "acp_asoc_acp63"; chip->rsrc = &acp63_rsrc; chip->acp_hw_ops_init = acp63_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp63_acp_machines; + chip->machines = snd_soc_acpi_amd_acp63_acp_machines; break; case 0x70: case 0x71: chip->name = "acp_asoc_acp70"; chip->rsrc = &acp70_rsrc; chip->acp_hw_ops_init = acp70_hw_ops_init; - chip->machines = &snd_soc_acpi_amd_acp70_acp_machines; + chip->machines = snd_soc_acpi_amd_acp70_acp_machines; break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c index d95047d2ee945..27da2a862f1c2 100644 --- a/sound/soc/amd/acp/amd-acpi-mach.c +++ b/sound/soc/amd/acp/amd-acpi-mach.c @@ -8,12 +8,12 @@ #include -struct snd_soc_acpi_codecs amp_rt1019 = { +static struct snd_soc_acpi_codecs amp_rt1019 = { .num_codecs = 1, .codecs = {"10EC1019"} }; -struct snd_soc_acpi_codecs amp_max = { +static struct snd_soc_acpi_codecs amp_max = { .num_codecs = 1, .codecs = {"MX98360A"} }; diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 863e74fcee437..cb8d97122f95c 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -243,10 +243,10 @@ extern struct acp_resource rmb_rsrc; extern struct acp_resource acp63_rsrc; extern struct acp_resource acp70_rsrc; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines; -extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[]; extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; extern const struct snd_soc_dai_ops acp_dmic_dai_ops; -- GitLab From 6ba68e5aa9d5d15c8877a655db279fcfc0b38b04 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:25 +0200 Subject: [PATCH 074/868] ASoC: renesas: msiof: Convert to Convert the MSIOF I2S driver to reuse the MSIOF register and register bit definitions in the header file shared by the MSIOF SPI driver. Signed-off-by: Geert Uytterhoeven Tested-by: Kuninori Morimoto Link: https://patch.msgid.link/754ed54057e54effd06143e71d6cd305c3334eca.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- sound/soc/renesas/rcar/msiof.c | 94 ++++++++++------------------------ 1 file changed, 28 insertions(+), 66 deletions(-) diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c index 75c9e91bada10..36d31ab8ac6a5 100644 --- a/sound/soc/renesas/rcar/msiof.c +++ b/sound/soc/renesas/rcar/msiof.c @@ -30,56 +30,15 @@ #include #include #include +#include #include #include -/* register */ -#define SITMDR1 0x00 -#define SITMDR2 0x04 -#define SITMDR3 0x08 -#define SIRMDR1 0x10 -#define SIRMDR2 0x14 -#define SIRMDR3 0x18 -#define SICTR 0x28 -#define SISTR 0x40 -#define SIIER 0x44 -#define SITFDR 0x50 -#define SIRFDR 0x60 - -/* SITMDR1/ SIRMDR1 */ -#define PCON (1 << 30) /* Transfer Signal Connection */ -#define SYNCMD_LR (3 << 28) /* L/R mode */ -#define SYNCAC (1 << 25) /* Sync Polarity (Active-low) */ -#define DTDL_1 (1 << 20) /* 1-clock-cycle delay */ -#define TXSTP (1 << 0) /* Transmission/Reception Stop on FIFO */ - -/* SITMDR2 and SIRMDR2 */ -#define BITLEN1(x) (((x) - 1) << 24) /* Data Size (8-32 bits) */ -#define GRP (1 << 30) /* Group count */ - -/* SICTR */ -#define TEDG (1 << 27) /* Transmit Timing (1 = falling edge) */ -#define REDG (1 << 26) /* Receive Timing (1 = rising edge) */ -#define TXE (1 << 9) /* Transmit Enable */ -#define RXE (1 << 8) /* Receive Enable */ - /* SISTR */ -#define TFSERR (1 << 21) /* Transmit Frame Synchronization Error */ -#define TFOVF (1 << 20) /* Transmit FIFO Overflow */ -#define TFUDF (1 << 19) /* Transmit FIFO Underflow */ -#define RFSERR (1 << 5) /* Receive Frame Synchronization Error */ -#define RFUDF (1 << 4) /* Receive FIFO Underflow */ -#define RFOVF (1 << 3) /* Receive FIFO Overflow */ -#define SISTR_ERR_TX (TFSERR | TFOVF | TFUDF) -#define SISTR_ERR_RX (RFSERR | RFOVF | RFUDF) +#define SISTR_ERR_TX (SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF) +#define SISTR_ERR_RX (SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF) #define SISTR_ERR (SISTR_ERR_TX | SISTR_ERR_RX) -/* SIIER */ -#define TDMAE (1 << 31) /* Transmit Data DMA Transfer Req. Enable */ -#define TDREQE (1 << 28) /* Transmit Data Transfer Request Enable */ -#define RDMAE (1 << 15) /* Receive Data DMA Transfer Req. Enable */ -#define RDREQE (1 << 12) /* Receive Data Transfer Request Enable */ - /* * The data on memory in 24bit case is located at side * [ xxxxxx] @@ -174,42 +133,45 @@ static int msiof_hw_start(struct snd_soc_component *component, /* SITMDRx */ if (is_play) { - val = PCON | SYNCMD_LR | SYNCAC | TXSTP; + val = SITMDR1_PCON | + FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | + SIMDR1_SYNCAC | SIMDR1_XXSTP; if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) - val |= DTDL_1; + val |= FIELD_PREP(SIMDR1_DTDL, 1); msiof_write(priv, SITMDR1, val); - val = BITLEN1(width); - msiof_write(priv, SITMDR2, val | GRP); + val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); + msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SITMDR3, val); } /* SIRMDRx */ else { - val = SYNCMD_LR | SYNCAC; + val = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) | + SIMDR1_SYNCAC; if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY)) - val |= DTDL_1; + val |= FIELD_PREP(SIMDR1_DTDL, 1); msiof_write(priv, SIRMDR1, val); - val = BITLEN1(width); - msiof_write(priv, SIRMDR2, val | GRP); + val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); + msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SIRMDR3, val); } /* SIIER */ if (is_play) - val = TDREQE | TDMAE | SISTR_ERR_TX; + val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX; else - val = RDREQE | RDMAE | SISTR_ERR_RX; + val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, val); /* SICTR */ if (is_play) - val = TXE | TEDG; + val = SICTR_TXE | SICTR_TEDG; else - val = RXE | REDG; + val = SICTR_RXE | SICTR_REDG; msiof_update_and_wait(priv, SICTR, val, val, val); msiof_status_clear(priv); @@ -230,9 +192,9 @@ static int msiof_hw_stop(struct snd_soc_component *component, /* SIIER */ if (is_play) - val = TDREQE | TDMAE | SISTR_ERR_TX; + val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX; else - val = RDREQE | RDMAE | SISTR_ERR_RX; + val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX; msiof_update(priv, SIIER, val, 0); /* Stop DMAC */ @@ -240,9 +202,9 @@ static int msiof_hw_stop(struct snd_soc_component *component, /* SICTR */ if (is_play) - val = TXE; + val = SICTR_TXE; else - val = RXE; + val = SICTR_RXE; msiof_update_and_wait(priv, SICTR, val, 0, 0); /* indicate error status if exist */ @@ -478,22 +440,22 @@ static irqreturn_t msiof_interrupt(int irq, void *data) substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK]; if (substream && (sistr & SISTR_ERR_TX)) { // snd_pcm_stop_xrun(substream); - if (sistr & TFSERR) + if (sistr & SISTR_TFSERR) priv->err_syc[SNDRV_PCM_STREAM_PLAYBACK]++; - if (sistr & TFOVF) + if (sistr & SISTR_TFOVF) priv->err_ovf[SNDRV_PCM_STREAM_PLAYBACK]++; - if (sistr & TFUDF) + if (sistr & SISTR_TFUDF) priv->err_udf[SNDRV_PCM_STREAM_PLAYBACK]++; } substream = priv->substream[SNDRV_PCM_STREAM_CAPTURE]; if (substream && (sistr & SISTR_ERR_RX)) { // snd_pcm_stop_xrun(substream); - if (sistr & RFSERR) + if (sistr & SISTR_RFSERR) priv->err_syc[SNDRV_PCM_STREAM_CAPTURE]++; - if (sistr & RFOVF) + if (sistr & SISTR_RFOVF) priv->err_ovf[SNDRV_PCM_STREAM_CAPTURE]++; - if (sistr & RFUDF) + if (sistr & SISTR_RFUDF) priv->err_udf[SNDRV_PCM_STREAM_CAPTURE]++; } -- GitLab From d6fa0ca959db8efd4462d7beef4bdc5568640fd0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Jun 2025 22:02:06 +0200 Subject: [PATCH 075/868] regulator: rpi-panel-v2: Add missing GPIOLIB dependency Add missing GPIOLIB dependency reported by the LKP test robot. This fixes the following report: " kismet warnings: (new ones prefixed by >>) >> kismet: WARNING: unmet direct dependencies detected for GPIO_REGMAP when selected by REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 WARNING: unmet direct dependencies detected for GPIO_REGMAP Depends on [n]: GPIOLIB [=n] Selected by [y]: - REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 [=y] && REGULATOR [=y] && I2C [=y] " Fixes: d49305862fdc ("regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506092341.enbNKMOR-lkp@intel.com/ Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609200242.31271-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 21ad6d938e4d4..9a3dc883ff403 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1155,6 +1155,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" + depends on GPIOLIB depends on I2C select GPIO_REGMAP select REGMAP_I2C -- GitLab From 08894232efa4b53e7cd064450a6d444b92ab24ae Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 22 May 2025 18:37:41 -0400 Subject: [PATCH 076/868] dt-bindings: gpio: convert gpio-pisosr.txt to yaml format Covert gpio-pisosr.txt to yaml format. Additional changes: - Add ref to spi-peripheral-props.yaml. - Set ngpios max value to 32. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250522223742.516254-1-Frank.Li@nxp.com Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-pisosr.txt | 34 ---------- .../devicetree/bindings/gpio/pisosr-gpio.yaml | 67 +++++++++++++++++++ 2 files changed, 67 insertions(+), 34 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-pisosr.txt create mode 100644 Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt b/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt deleted file mode 100644 index fba3c61f6a5bc..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt +++ /dev/null @@ -1,34 +0,0 @@ -Generic Parallel-in/Serial-out Shift Register GPIO Driver - -This binding describes generic parallel-in/serial-out shift register -devices that can be used for GPI (General Purpose Input). This includes -SN74165 serial-out shift registers and the SN65HVS88x series of -industrial serializers. - -Required properties: - - compatible : Should be "pisosr-gpio". - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Optional properties: - - ngpios : Number of used GPIO lines (0..n-1), default is 8. - - load-gpios : GPIO pin specifier attached to load enable, this - pin is pulsed before reading from the device to - load input pin values into the device. - -For other required and optional properties of SPI slave -nodes please refer to ../spi/spi-bus.txt. - -Example: - - gpio@0 { - compatible = "ti,sn65hvs882", "pisosr-gpio"; - gpio-controller; - #gpio-cells = <2>; - - load-gpios = <&gpio2 23 GPIO_ACTIVE_LOW>; - - reg = <0>; - spi-max-frequency = <1000000>; - spi-cpol; - }; diff --git a/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml b/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml new file mode 100644 index 0000000000000..db98ba413fb6b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/pisosr-gpio.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/pisosr-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic Parallel-in/Serial-out Shift Register GPIO Driver + +description: + This binding describes generic parallel-in/serial-out shift register + devices that can be used for GPI (General Purpose Input). This includes + SN74165 serial-out shift registers and the SN65HVS88x series of + industrial serializers. + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - pisosr-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ngpios: + maximum: 32 + default: 8 + + load-gpios: + description: + GPIO pin specifier attached to load enable, this + pin is pulsed before reading from the device to + load input pin values into the device. + + spi-cpol: true + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@0 { + compatible = "pisosr-gpio"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + load-gpios = <&gpio2 23 GPIO_ACTIVE_LOW>; + spi-max-frequency = <1000000>; + spi-cpol; + }; + }; -- GitLab From 32f6d31dc0401e6af7c48e5e2381997b6d957d85 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 1 Jun 2025 00:21:38 +0300 Subject: [PATCH 077/868] usb: gadget: pxa25x_udc: Switch to use devm_gpio_request_one() devm_gpio_request() is going to be removed. This driver is only user of that API. Convert it to use different API. Signed-off-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250531212331.3635269-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/usb/gadget/udc/pxa25x_udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index f5d09a91e5546..b97fb7b0cb2c2 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2348,15 +2348,14 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { - retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, - "pca25x_udc GPIO PULLUP"); + retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup, + GPIOF_OUT_INIT_LOW, "pca25x_udc GPIO PULLUP"); if (retval) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); goto err; } - gpio_direction_output(dev->mach->gpio_pullup, 0); } timer_setup(&dev->timer, udc_watchdog, 0); -- GitLab From a5589313383074c48a1b3751d592a6e084ae0573 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 1 Jun 2025 00:21:39 +0300 Subject: [PATCH 078/868] gpiolib: Remove unused devm_gpio_request() Remove devm_gpio_request() due to lack of users. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250531212331.3635269-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- .../driver-api/driver-model/devres.rst | 1 - drivers/gpio/gpiolib-legacy.c | 38 ------------------- include/linux/gpio.h | 8 ---- 3 files changed, 47 deletions(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 3d56f94ac2eef..2b36ebde9cece 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -275,7 +275,6 @@ GPIO devm_gpiod_put() devm_gpiod_unhinge() devm_gpiochip_add_data() - devm_gpio_request() devm_gpio_request_one() I2C diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index aeae6df8bec99..3bc93ccadb5b5 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -85,44 +85,6 @@ static void devm_gpio_release(struct device *dev, void *res) gpio_free(*gpio); } -/** - * devm_gpio_request - request a GPIO for a managed device - * @dev: device to request the GPIO for - * @gpio: GPIO to allocate - * @label: the name of the requested GPIO - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_request(). - * GPIOs requested with this function will be automatically freed - * on driver detach. - * - * **DEPRECATED** This function is deprecated and must not be used in new code. - * - * Returns: - * 0 on success, or negative errno on failure. - */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) -{ - unsigned *dr; - int rc; - - dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL); - if (!dr) - return -ENOMEM; - - rc = gpio_request(gpio, label); - if (rc) { - devres_free(dr); - return rc; - } - - *dr = gpio; - devres_add(dev, dr); - - return 0; -} -EXPORT_SYMBOL_GPL(devm_gpio_request); - /** * devm_gpio_request_one - request a single GPIO with initial setup * @dev: device to request for diff --git a/include/linux/gpio.h b/include/linux/gpio.h index c1ec62c11ed3c..61e9f43c082ae 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -110,7 +110,6 @@ static inline int gpio_to_irq(unsigned gpio) int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label); @@ -188,13 +187,6 @@ static inline int gpio_to_irq(unsigned gpio) return -EINVAL; } -static inline int devm_gpio_request(struct device *dev, unsigned gpio, - const char *label) -{ - WARN_ON(1); - return -EINVAL; -} - static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label) { -- GitLab From 0e3b7b8759a7f3597e64fc12a8a017111edbf777 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 4 Jun 2025 10:46:30 -0400 Subject: [PATCH 079/868] dt-bindings: gpio: convert gpio-74xx-mmio.txt to yaml format Convert gpio-74xx-mmio.txt to yaml format. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20250604144631.1141430-1-Frank.Li@nxp.com Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/gpio-74xx-mmio.txt | 30 ---------- .../devicetree/bindings/gpio/ti,7416374.yaml | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 30 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt create mode 100644 Documentation/devicetree/bindings/gpio/ti,7416374.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt b/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt deleted file mode 100644 index 7bb1a9d601331..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-74xx-mmio.txt +++ /dev/null @@ -1,30 +0,0 @@ -* 74XX MMIO GPIO driver - -Required properties: -- compatible: Should contain one of the following: - "ti,741g125": for 741G125 (1-bit Input), - "ti,741g174": for 741G74 (1-bit Output), - "ti,742g125": for 742G125 (2-bit Input), - "ti,7474" : for 7474 (2-bit Output), - "ti,74125" : for 74125 (4-bit Input), - "ti,74175" : for 74175 (4-bit Output), - "ti,74365" : for 74365 (6-bit Input), - "ti,74174" : for 74174 (6-bit Output), - "ti,74244" : for 74244 (8-bit Input), - "ti,74273" : for 74273 (8-bit Output), - "ti,741624" : for 741624 (16-bit Input), - "ti,7416374": for 7416374 (16-bit Output). -- reg: Physical base address and length where IC resides. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the GPIO polarity: - 0 = Active High, - 1 = Active Low. - -Example: - ctrl: gpio@30008004 { - compatible = "ti,74174"; - reg = <0x30008004 0x1>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/ti,7416374.yaml b/Documentation/devicetree/bindings/gpio/ti,7416374.yaml new file mode 100644 index 0000000000000..33472f0911011 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/ti,7416374.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/ti,7416374.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI 74XX MMIO GPIO + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - ti,741g125 # for 741G125 (1-bit Input), + - ti,741g174 # for 741G74 (1-bit Output), + - ti,742g125 # for 742G125 (2-bit Input), + - ti,7474 # for 7474 (2-bit Output), + - ti,74125 # for 74125 (4-bit Input), + - ti,74175 # for 74175 (4-bit Output), + - ti,74365 # for 74365 (6-bit Input), + - ti,74174 # for 74174 (6-bit Output), + - ti,74244 # for 74244 (8-bit Input), + - ti,74273 # for 74273 (8-bit Output), + - ti,741624 # for 741624 (16-bit Input), + - ti,7416374 # for 7416374 (16-bit Output). + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + description: | + The first cell is the pin number and + the second cell is used to specify the GPIO polarity: + 0 = Active High, + 1 = Active Low. + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + gpio@30008004 { + compatible = "ti,74174"; + reg = <0x30008004 0x1>; + gpio-controller; + #gpio-cells = <2>; + }; -- GitLab From 9b4d4c952e28f97c5e653c8b9453690f7e63cc5a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 31 May 2025 22:55:43 +0300 Subject: [PATCH 080/868] gpio: Remove unused 'struct gpio' definition There is no user for the legacy 'struct gpio', remove it for good. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250531195801.3632110-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- include/linux/gpio.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 61e9f43c082ae..8697ab8178981 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -21,18 +21,6 @@ struct device; #define GPIOF_OUT_INIT_LOW ((0 << 0) | (0 << 1)) #define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1)) -/** - * struct gpio - a structure describing a GPIO with configuration - * @gpio: the GPIO number - * @flags: GPIO configuration as specified by GPIOF_* - * @label: a literal description string of this GPIO - */ -struct gpio { - unsigned gpio; - unsigned long flags; - const char *label; -}; - #ifdef CONFIG_GPIOLIB #include -- GitLab From 6595ea2761df191c2ec500d5f54b57592b969f5c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 31 May 2025 22:55:44 +0300 Subject: [PATCH 081/868] gpiolib: Move GPIO_DYNAMIC_* constants to its only user There is no need to export GPIO_DYNAMIC_* constants, especially via legacy header which is subject to remove. Move the mentioned constants to its only user, i.e. gpiolib.c. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250531195801.3632110-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 13 +++++++++++++ include/linux/gpio.h | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fdafa0df1b432..5b0b4fc975435 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -74,6 +74,19 @@ static const struct bus_type gpio_bus_type = { .match = gpio_bus_match, }; +/* + * At the end we want all GPIOs to be dynamically allocated from 0. + * However, some legacy drivers still perform fixed allocation. + * Until they are all fixed, leave 0-512 space for them. + */ +#define GPIO_DYNAMIC_BASE 512 +/* + * Define the maximum of the possible GPIO in the global numberspace. + * While the GPIO base and numbers are positive, we limit it with signed + * maximum as a lot of code is using negative values for special cases. + */ +#define GPIO_DYNAMIC_MAX INT_MAX + /* * Number of GPIOs to use for the fast path in set array */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 8697ab8178981..ff99ed76fdc33 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -45,19 +45,6 @@ static inline bool gpio_is_valid(int number) * extra memory (for code and for per-GPIO table entries). */ -/* - * At the end we want all GPIOs to be dynamically allocated from 0. - * However, some legacy drivers still perform fixed allocation. - * Until they are all fixed, leave 0-512 space for them. - */ -#define GPIO_DYNAMIC_BASE 512 -/* - * Define the maximum of the possible GPIO in the global numberspace. - * While the GPIO base and numbers are positive, we limit it with signed - * maximum as a lot of code is using negative values for special cases. - */ -#define GPIO_DYNAMIC_MAX INT_MAX - /* Always use the library code for GPIO management calls, * or when sleeping may be involved. */ -- GitLab From 114ab5afdf6640d40c8982170ea78c127c983dad Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 2 Jun 2025 10:42:58 -0400 Subject: [PATCH 082/868] dt-bindings: gpio: convert nxp,lpc1850-gpio.txt to yaml format Convert nxp,lpc1850-gpio.txt to yaml format. Additional changes: - remove interrupt-controller and #interupt-cells from required list to match existed dts files. - remove gpio consumer in examples. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250602144259.944257-1-Frank.Li@nxp.com Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/nxp,lpc1850-gpio.txt | 59 -------------- .../bindings/gpio/nxp,lpc1850-gpio.yaml | 78 +++++++++++++++++++ 2 files changed, 78 insertions(+), 59 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt deleted file mode 100644 index 627efc78ecf2d..0000000000000 --- a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt +++ /dev/null @@ -1,59 +0,0 @@ -NXP LPC18xx/43xx GPIO controller Device Tree Bindings ------------------------------------------------------ - -Required properties: -- compatible : Should be "nxp,lpc1850-gpio" -- reg : List of addresses and lengths of the GPIO controller - register sets -- reg-names : Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and - "gpio-gpoup1-ic" -- clocks : Phandle and clock specifier pair for GPIO controller -- resets : Phandle and reset specifier pair for GPIO controller -- gpio-controller : Marks the device node as a GPIO controller -- #gpio-cells : Should be two: - - The first cell is the GPIO line number - - The second cell is used to specify polarity -- interrupt-controller : Marks the device node as an interrupt controller -- #interrupt-cells : Should be two: - - The first cell is an interrupt number within - 0..9 range, for GPIO pin interrupts it is equal - to 'nxp,gpio-pin-interrupt' property value of - GPIO pin configuration, 8 is for GPIO GROUP0 - interrupt, 9 is for GPIO GROUP1 interrupt - - The second cell is used to specify interrupt type - -Optional properties: -- gpio-ranges : Mapping between GPIO and pinctrl - -Example: -#define LPC_GPIO(port, pin) (port * 32 + pin) -#define LPC_PIN(port, pin) (0x##port * 32 + pin) - -gpio: gpio@400f4000 { - compatible = "nxp,lpc1850-gpio"; - reg = <0x400f4000 0x4000>, <0x40087000 0x1000>, - <0x40088000 0x1000>, <0x40089000 0x1000>; - reg-names = "gpio", "gpio-pin-ic", - "gpio-group0-ic", "gpio-gpoup1-ic"; - clocks = <&ccu1 CLK_CPU_GPIO>; - resets = <&rgu 28>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>, - ... - <&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>; -}; - -gpio_joystick { - compatible = "gpio-keys"; - ... - - button0 { - ... - interrupt-parent = <&gpio>; - interrupts = <1 IRQ_TYPE_EDGE_BOTH>; - gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml new file mode 100644 index 0000000000000..0ef5f90f69ff3 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/nxp,lpc1850-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx/43xx GPIO controller + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-gpio + + reg: + minItems: 1 + maxItems: 4 + + reg-names: + minItems: 1 + items: + - const: gpio + - const: gpio-pin-ic + - const: gpio-group0-ic + - const: gpio-gpoup1-ic + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + description: | + - The first cell is an interrupt number within + 0..9 range, for GPIO pin interrupts it is equal + to 'nxp,gpio-pin-interrupt' property value of + GPIO pin configuration, 8 is for GPIO GROUP0 + interrupt, 9 is for GPIO GROUP1 interrupt + - The second cell is used to specify interrupt type + + gpio-ranges: true + +required: + - compatible + - reg + - clocks + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + #include + + gpio@400f4000 { + compatible = "nxp,lpc1850-gpio"; + reg = <0x400f4000 0x4000>, <0x40087000 0x1000>, + <0x40088000 0x1000>, <0x40089000 0x1000>; + reg-names = "gpio", "gpio-pin-ic", "gpio-group0-ic", "gpio-gpoup1-ic"; + clocks = <&ccu1 CLK_CPU_GPIO>; + resets = <&rgu 28>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + -- GitLab From 97a7ea2b8f4a9aec1f43435658343e046c2a4983 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 27 May 2025 13:13:53 +0200 Subject: [PATCH 083/868] gpio: TODO: add a task for removing MMIO-specific fields from gpio_chip Currently for CONFIG_GPIO_GENERIC=y each struct gpio_chip object contains the fields relevant only for gpio-mmio users. It's not an insignificant number either as it's several pointers and integers. It makes sense to remove these fields from struct gpio_chip into a dedicated structure but this is not trivial due to how the bgpio_init() function is implemented. Add a task for tracking this rework. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250527111353.71540-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 4a8b349f2483a..ef53892cb44d7 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -131,6 +131,11 @@ Work items: helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use this with dry-coding and sending to maintainers to test +- Move the MMIO GPIO specific fields out of struct gpio_chip into a + dedicated structure. Currently every GPIO chip has them if gpio-mmio is + enabled in Kconfig even if it itself doesn't register with the helper + library. + ------------------------------------------------------------------------------- Generic regmap GPIO -- GitLab From 7e10d7242ea8a5947878880b912ffa5806520705 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 Jun 2025 11:30:53 +0200 Subject: [PATCH 084/868] ASoC: ops: dynamically allocate struct snd_ctl_elem_value This structure is really too larget to be allocated on the stack: sound/soc/soc-ops.c:435:5: error: stack frame size (1296) exceeds limit (1280) in 'snd_soc_limit_volume' [-Werror,-Wframe-larger-than] Change the function to dynamically allocate it instead. There is probably a better way to do it since only two integer fields inside of that structure are actually used, but this is the simplest rework for the moment. Fixes: 783db6851c18 ("ASoC: ops: Enforce platform maximum on initial value") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20250610093057.2643233-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 8d4dd11c9aef1..a629e0eacb20e 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -399,28 +399,32 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); static int snd_soc_clip_to_platform_max(struct snd_kcontrol *kctl) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; - struct snd_ctl_elem_value uctl; + struct snd_ctl_elem_value *uctl; int ret; if (!mc->platform_max) return 0; - ret = kctl->get(kctl, &uctl); + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; + + ret = kctl->get(kctl, uctl); if (ret < 0) - return ret; + goto out; - if (uctl.value.integer.value[0] > mc->platform_max) - uctl.value.integer.value[0] = mc->platform_max; + if (uctl->value.integer.value[0] > mc->platform_max) + uctl->value.integer.value[0] = mc->platform_max; if (snd_soc_volsw_is_stereo(mc) && - uctl.value.integer.value[1] > mc->platform_max) - uctl.value.integer.value[1] = mc->platform_max; + uctl->value.integer.value[1] > mc->platform_max) + uctl->value.integer.value[1] = mc->platform_max; - ret = kctl->put(kctl, &uctl); - if (ret < 0) - return ret; + ret = kctl->put(kctl, uctl); - return 0; +out: + kfree(uctl); + return ret; } /** -- GitLab From d6e2c062e5f154d1c54022b8defb91c21648562d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 10 Jun 2025 14:18:33 +0200 Subject: [PATCH 085/868] ALSA: mips/sgio2audio: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250610121835.2908-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/mips/sgio2audio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index 4e2ff954ff590..1af177f25c68f 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -685,7 +686,7 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) return err; pcm->private_data = chip; - strcpy(pcm->name, "SGI O2 DAC1"); + strscpy(pcm->name, "SGI O2 DAC1"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -700,7 +701,7 @@ static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) return err; pcm->private_data = chip; - strcpy(pcm->name, "SGI O2 DAC2"); + strscpy(pcm->name, "SGI O2 DAC2"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -892,8 +893,8 @@ static int snd_sgio2audio_probe(struct platform_device *pdev) return err; } - strcpy(card->driver, "SGI O2 Audio"); - strcpy(card->shortname, "SGI O2 Audio"); + strscpy(card->driver, "SGI O2 Audio"); + strscpy(card->shortname, "SGI O2 Audio"); sprintf(card->longname, "%s irq %i-%i", card->shortname, MACEISA_AUDIO1_DMAT_IRQ, -- GitLab From 165bb5b154de4c39c3aa9962f0dd69836d030c19 Mon Sep 17 00:00:00 2001 From: Lucy Thrun Date: Tue, 10 Jun 2025 19:50:11 +0200 Subject: [PATCH 086/868] ALSA: hda/ca0132: Fix using plain integer as NULL pointer in add_tuning_control The 'add_tuning_control' function initializes two pointers using the integer 0 instead of the NULL pointer, triggering sparse warnings. Replaced both instaces of '0' with 'NULL' to resolve the type mismatch and suppress the sparse warnings. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506100842.d8lwwdwU-lkp@intel.com/ Signed-off-by: Lucy Thrun Link: https://patch.msgid.link/20250610175012.918-2-lucy.thrun@digital-rabbithole.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cfe422a797033..491d45d652dd0 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4385,8 +4385,8 @@ static int add_tuning_control(struct hda_codec *codec, knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; - knew.tlv.c = 0; - knew.tlv.p = 0; + knew.tlv.c = NULL; + knew.tlv.p = NULL; switch (pnid) { case VOICE_FOCUS: knew.info = voice_focus_ctl_info; -- GitLab From a409c60111e6bb98fcabab2aeaa069daa9434ca0 Mon Sep 17 00:00:00 2001 From: Lucy Thrun Date: Tue, 10 Jun 2025 19:50:12 +0200 Subject: [PATCH 087/868] ALSA: hda/ca0132: Fix buffer overflow in add_tuning_control The 'sprintf' call in 'add_tuning_control' may exceed the 44-byte buffer if either string argument is too long. This triggers a compiler warning. Replaced 'sprintf' with 'snprintf' to limit string lengths to prevent overflow. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506100642.95jpuMY1-lkp@intel.com/ Signed-off-by: Lucy Thrun Link: https://patch.msgid.link/20250610175012.918-3-lucy.thrun@digital-rabbithole.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 491d45d652dd0..1904964591dbc 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4410,7 +4410,7 @@ static int add_tuning_control(struct hda_codec *codec, } knew.private_value = HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); - sprintf(namestr, "%s %s Volume", name, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "%s %s Volume", name, dirstr[dir]); return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } -- GitLab From 8866f4e557eba43e991f99711515217a95f62d2e Mon Sep 17 00:00:00 2001 From: Tomasz Michalec Date: Tue, 10 Jun 2025 17:37:47 +0200 Subject: [PATCH 088/868] platform/chrome: cros_ec_typec: Defer probe on missing EC parent If cros_typec_probe is called before EC device is registered, cros_typec_probe will fail. It may happen when cros-ec-typec.ko is loaded before EC bus layer module (e.g. cros_ec_lpcs.ko, cros_ec_spi.ko). Return -EPROBE_DEFER when cros_typec_probe doesn't get EC device, so the probe function can be called again after EC device is registered. Signed-off-by: Tomasz Michalec Reviewed-by: Abhishek Pandit-Subedi Link: https://lore.kernel.org/r/20250610153748.1858519-1-tmichalec@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_typec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 7678e3d05fd36..f437b594055cf 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -1272,8 +1272,8 @@ static int cros_typec_probe(struct platform_device *pdev) typec->ec = dev_get_drvdata(pdev->dev.parent); if (!typec->ec) { - dev_err(dev, "couldn't find parent EC device\n"); - return -ENODEV; + dev_warn(dev, "couldn't find parent EC device\n"); + return -EPROBE_DEFER; } platform_set_drvdata(pdev, typec); -- GitLab From 3dd1e9c2a279cbc9c6a7f1e7961df08c76ecb464 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 8 Jun 2025 02:25:10 +0100 Subject: [PATCH 089/868] platform/x86: intel_telemetry: Remove unused telemetry_*_events() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions: - telemetry_add_events() - telemetry_update_events() - telemetry_reset_events() - telemetry_get_eventconfig() were all added by the commit 378f956e3f93 ("platform/x86: Add Intel Telemetry Core Driver") in 2016 but have remained unused. They're each a tiny wrapper that is the only caller through a similarly named function pointer, and for each function pointer there's a 'def' empty implementation and a plt implementation. Remove all of those components for each function. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250608012512.377134-2-linux@treblig.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/include/asm/intel_telemetry.h | 24 ---- drivers/platform/x86/intel/telemetry/core.c | 101 ---------------- drivers/platform/x86/intel/telemetry/pltdrv.c | 114 ------------------ 3 files changed, 239 deletions(-) diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h index 43b7657febcaa..3d7e0b922341b 100644 --- a/arch/x86/include/asm/intel_telemetry.h +++ b/arch/x86/include/asm/intel_telemetry.h @@ -62,13 +62,6 @@ struct telemetry_core_ops { int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period, u8 *ioss_min_period, u8 *ioss_max_period); - int (*get_eventconfig)(struct telemetry_evtconfig *pss_evtconfig, - struct telemetry_evtconfig *ioss_evtconfig, - int pss_len, int ioss_len); - - int (*update_events)(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig); - int (*set_sampling_period)(u8 pss_period, u8 ioss_period); int (*get_trace_verbosity)(enum telemetry_unit telem_unit, @@ -84,11 +77,6 @@ struct telemetry_core_ops { int (*read_eventlog)(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len, int log_all_evts); - - int (*add_events)(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap); - - int (*reset_events)(void); }; int telemetry_set_pltdata(const struct telemetry_core_ops *ops, @@ -101,18 +89,6 @@ struct telemetry_plt_config *telemetry_get_pltdata(void); int telemetry_get_evtname(enum telemetry_unit telem_unit, const char **name, int len); -int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig); - -int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap); - -int telemetry_reset_events(void); - -int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_config, - struct telemetry_evtconfig *ioss_config, - int pss_len, int ioss_len); - int telemetry_read_events(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); diff --git a/drivers/platform/x86/intel/telemetry/core.c b/drivers/platform/x86/intel/telemetry/core.c index e4be40f73eebf..229e59c64af73 100644 --- a/drivers/platform/x86/intel/telemetry/core.c +++ b/drivers/platform/x86/intel/telemetry/core.c @@ -21,12 +21,6 @@ struct telemetry_core_config { static struct telemetry_core_config telm_core_conf; -static int telemetry_def_update_events(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig) -{ - return 0; -} - static int telemetry_def_set_sampling_period(u8 pss_period, u8 ioss_period) { return 0; @@ -40,14 +34,6 @@ static int telemetry_def_get_sampling_period(u8 *pss_min_period, return 0; } -static int telemetry_def_get_eventconfig( - struct telemetry_evtconfig *pss_evtconfig, - struct telemetry_evtconfig *ioss_evtconfig, - int pss_len, int ioss_len) -{ - return 0; -} - static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit, u32 *verbosity) { @@ -75,51 +61,15 @@ static int telemetry_def_read_eventlog(enum telemetry_unit telem_unit, return 0; } -static int telemetry_def_add_events(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap) -{ - return 0; -} - -static int telemetry_def_reset_events(void) -{ - return 0; -} - static const struct telemetry_core_ops telm_defpltops = { .set_sampling_period = telemetry_def_set_sampling_period, .get_sampling_period = telemetry_def_get_sampling_period, .get_trace_verbosity = telemetry_def_get_trace_verbosity, .set_trace_verbosity = telemetry_def_set_trace_verbosity, .raw_read_eventlog = telemetry_def_raw_read_eventlog, - .get_eventconfig = telemetry_def_get_eventconfig, .read_eventlog = telemetry_def_read_eventlog, - .update_events = telemetry_def_update_events, - .reset_events = telemetry_def_reset_events, - .add_events = telemetry_def_add_events, }; -/** - * telemetry_update_events() - Update telemetry Configuration - * @pss_evtconfig: PSS related config. No change if num_evts = 0. - * @ioss_evtconfig: IOSS related config. No change if num_evts = 0. - * - * This API updates the IOSS & PSS Telemetry configuration. Old config - * is overwritten. Call telemetry_reset_events when logging is over - * All sample period values should be in the form of: - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) - * - * Return: 0 success, < 0 for failure - */ -int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig) -{ - return telm_core_conf.telem_ops->update_events(pss_evtconfig, - ioss_evtconfig); -} -EXPORT_SYMBOL_GPL(telemetry_update_events); - - /** * telemetry_set_sampling_period() - Sets the IOSS & PSS sampling period * @pss_period: placeholder for PSS Period to be set. @@ -162,57 +112,6 @@ int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, EXPORT_SYMBOL_GPL(telemetry_get_sampling_period); -/** - * telemetry_reset_events() - Restore the IOSS & PSS configuration to default - * - * Return: 0 success, < 0 for failure - */ -int telemetry_reset_events(void) -{ - return telm_core_conf.telem_ops->reset_events(); -} -EXPORT_SYMBOL_GPL(telemetry_reset_events); - -/** - * telemetry_get_eventconfig() - Returns the pss and ioss events enabled - * @pss_evtconfig: Pointer to PSS related configuration. - * @ioss_evtconfig: Pointer to IOSS related configuration. - * @pss_len: Number of u32 elements allocated for pss_evtconfig array - * @ioss_len: Number of u32 elements allocated for ioss_evtconfig array - * - * Return: 0 success, < 0 for failure - */ -int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_evtconfig, - struct telemetry_evtconfig *ioss_evtconfig, - int pss_len, int ioss_len) -{ - return telm_core_conf.telem_ops->get_eventconfig(pss_evtconfig, - ioss_evtconfig, - pss_len, ioss_len); -} -EXPORT_SYMBOL_GPL(telemetry_get_eventconfig); - -/** - * telemetry_add_events() - Add IOSS & PSS configuration to existing settings. - * @num_pss_evts: Number of PSS Events (<29) in pss_evtmap. Can be 0. - * @num_ioss_evts: Number of IOSS Events (<29) in ioss_evtmap. Can be 0. - * @pss_evtmap: Array of PSS Event-IDs to Enable - * @ioss_evtmap: Array of PSS Event-IDs to Enable - * - * Events are appended to Old Configuration. In case of total events > 28, it - * returns error. Call telemetry_reset_events to reset after eventlog done - * - * Return: 0 success, < 0 for failure - */ -int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap) -{ - return telm_core_conf.telem_ops->add_events(num_pss_evts, - num_ioss_evts, pss_evtmap, - ioss_evtmap); -} -EXPORT_SYMBOL_GPL(telemetry_add_events); - /** * telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id * @telem_unit: Specify whether IOSS or PSS Read diff --git a/drivers/platform/x86/intel/telemetry/pltdrv.c b/drivers/platform/x86/intel/telemetry/pltdrv.c index 9a499efa1e4d5..60d3783de7eff 100644 --- a/drivers/platform/x86/intel/telemetry/pltdrv.c +++ b/drivers/platform/x86/intel/telemetry/pltdrv.c @@ -639,32 +639,6 @@ static int telemetry_setup(struct platform_device *pdev) return 0; } -static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig, - struct telemetry_evtconfig ioss_evtconfig) -{ - int ret; - - if ((pss_evtconfig.num_evts > 0) && - (TELEM_SAMPLE_PERIOD_INVALID(pss_evtconfig.period))) { - pr_err("PSS Sampling Period Out of Range\n"); - return -EINVAL; - } - - if ((ioss_evtconfig.num_evts > 0) && - (TELEM_SAMPLE_PERIOD_INVALID(ioss_evtconfig.period))) { - pr_err("IOSS Sampling Period Out of Range\n"); - return -EINVAL; - } - - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, - TELEM_UPDATE); - if (ret) - pr_err("TELEMETRY Config Failed\n"); - - return ret; -} - - static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) { u32 telem_ctrl = 0; @@ -780,90 +754,6 @@ static int telemetry_plt_get_sampling_period(u8 *pss_min_period, } -static int telemetry_plt_reset_events(void) -{ - struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig; - int ret; - - pss_evtconfig.evtmap = NULL; - pss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS; - pss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD; - - ioss_evtconfig.evtmap = NULL; - ioss_evtconfig.num_evts = TELEM_MAX_OS_ALLOCATED_EVENTS; - ioss_evtconfig.period = TELEM_SAMPLING_DEFAULT_PERIOD; - - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, - TELEM_RESET); - if (ret) - pr_err("TELEMETRY Reset Failed\n"); - - return ret; -} - - -static int telemetry_plt_get_eventconfig(struct telemetry_evtconfig *pss_config, - struct telemetry_evtconfig *ioss_config, - int pss_len, int ioss_len) -{ - u32 *pss_evtmap, *ioss_evtmap; - u32 index; - - pss_evtmap = pss_config->evtmap; - ioss_evtmap = ioss_config->evtmap; - - mutex_lock(&(telm_conf->telem_lock)); - pss_config->num_evts = telm_conf->pss_config.ssram_evts_used; - ioss_config->num_evts = telm_conf->ioss_config.ssram_evts_used; - - pss_config->period = telm_conf->pss_config.curr_period; - ioss_config->period = telm_conf->ioss_config.curr_period; - - if ((pss_len < telm_conf->pss_config.ssram_evts_used) || - (ioss_len < telm_conf->ioss_config.ssram_evts_used)) { - mutex_unlock(&(telm_conf->telem_lock)); - return -EINVAL; - } - - for (index = 0; index < telm_conf->pss_config.ssram_evts_used; - index++) { - pss_evtmap[index] = - telm_conf->pss_config.telem_evts[index].evt_id; - } - - for (index = 0; index < telm_conf->ioss_config.ssram_evts_used; - index++) { - ioss_evtmap[index] = - telm_conf->ioss_config.telem_evts[index].evt_id; - } - - mutex_unlock(&(telm_conf->telem_lock)); - return 0; -} - - -static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts, - u32 *pss_evtmap, u32 *ioss_evtmap) -{ - struct telemetry_evtconfig pss_evtconfig, ioss_evtconfig; - int ret; - - pss_evtconfig.evtmap = pss_evtmap; - pss_evtconfig.num_evts = num_pss_evts; - pss_evtconfig.period = telm_conf->pss_config.curr_period; - - ioss_evtconfig.evtmap = ioss_evtmap; - ioss_evtconfig.num_evts = num_ioss_evts; - ioss_evtconfig.period = telm_conf->ioss_config.curr_period; - - ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, - TELEM_ADD); - if (ret) - pr_err("TELEMETRY ADD Failed\n"); - - return ret; -} - static int telem_evtlog_read(enum telemetry_unit telem_unit, struct telem_ssram_region *ssram_region, u8 len) { @@ -1096,11 +986,7 @@ static const struct telemetry_core_ops telm_pltops = { .set_sampling_period = telemetry_plt_set_sampling_period, .get_sampling_period = telemetry_plt_get_sampling_period, .raw_read_eventlog = telemetry_plt_raw_read_eventlog, - .get_eventconfig = telemetry_plt_get_eventconfig, - .update_events = telemetry_plt_update_events, .read_eventlog = telemetry_plt_read_eventlog, - .reset_events = telemetry_plt_reset_events, - .add_events = telemetry_plt_add_events, }; static int telemetry_pltdrv_probe(struct platform_device *pdev) -- GitLab From b35b9fb28c85ccee43c34768c5e2d9a5c510e660 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 8 Jun 2025 02:25:11 +0100 Subject: [PATCH 090/868] platform/x86: intel_telemetry: Remove unused telemetry_[gs]et_sampling_period() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions: - telemetry_get_sampling_period() - telemetry_set_sampling_period() were both added by the commit 378f956e3f93 ("platform/x86: Add Intel Telemetry Core Driver") in 2016 but have remained unused. They're each a tiny wrapper that is the only caller through a similarly named function pointer, and for each function pointer there's a 'def' empty implementation and a plt implementation. Remove all of those components for each function. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250608012512.377134-3-linux@treblig.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/include/asm/intel_telemetry.h | 10 -- drivers/platform/x86/intel/telemetry/core.c | 57 --------- drivers/platform/x86/intel/telemetry/pltdrv.c | 117 ------------------ 3 files changed, 184 deletions(-) diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h index 3d7e0b922341b..e7fb005ac8d86 100644 --- a/arch/x86/include/asm/intel_telemetry.h +++ b/arch/x86/include/asm/intel_telemetry.h @@ -59,11 +59,6 @@ struct telemetry_plt_config { }; struct telemetry_core_ops { - int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period, - u8 *ioss_min_period, u8 *ioss_max_period); - - int (*set_sampling_period)(u8 pss_period, u8 ioss_period); - int (*get_trace_verbosity)(enum telemetry_unit telem_unit, u32 *verbosity); @@ -101,11 +96,6 @@ int telemetry_read_eventlog(enum telemetry_unit telem_unit, int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); -int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, - u8 *ioss_min_period, u8 *ioss_max_period); - -int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period); - int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit, u32 verbosity); diff --git a/drivers/platform/x86/intel/telemetry/core.c b/drivers/platform/x86/intel/telemetry/core.c index 229e59c64af73..fe9e8580a8f52 100644 --- a/drivers/platform/x86/intel/telemetry/core.c +++ b/drivers/platform/x86/intel/telemetry/core.c @@ -21,19 +21,6 @@ struct telemetry_core_config { static struct telemetry_core_config telm_core_conf; -static int telemetry_def_set_sampling_period(u8 pss_period, u8 ioss_period) -{ - return 0; -} - -static int telemetry_def_get_sampling_period(u8 *pss_min_period, - u8 *pss_max_period, - u8 *ioss_min_period, - u8 *ioss_max_period) -{ - return 0; -} - static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit, u32 *verbosity) { @@ -62,56 +49,12 @@ static int telemetry_def_read_eventlog(enum telemetry_unit telem_unit, } static const struct telemetry_core_ops telm_defpltops = { - .set_sampling_period = telemetry_def_set_sampling_period, - .get_sampling_period = telemetry_def_get_sampling_period, .get_trace_verbosity = telemetry_def_get_trace_verbosity, .set_trace_verbosity = telemetry_def_set_trace_verbosity, .raw_read_eventlog = telemetry_def_raw_read_eventlog, .read_eventlog = telemetry_def_read_eventlog, }; -/** - * telemetry_set_sampling_period() - Sets the IOSS & PSS sampling period - * @pss_period: placeholder for PSS Period to be set. - * Set to 0 if not required to be updated - * @ioss_period: placeholder for IOSS Period to be set - * Set to 0 if not required to be updated - * - * All values should be in the form of: - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) - * - * Return: 0 success, < 0 for failure - */ -int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period) -{ - return telm_core_conf.telem_ops->set_sampling_period(pss_period, - ioss_period); -} -EXPORT_SYMBOL_GPL(telemetry_set_sampling_period); - -/** - * telemetry_get_sampling_period() - Get IOSS & PSS min & max sampling period - * @pss_min_period: placeholder for PSS Min Period supported - * @pss_max_period: placeholder for PSS Max Period supported - * @ioss_min_period: placeholder for IOSS Min Period supported - * @ioss_max_period: placeholder for IOSS Max Period supported - * - * All values should be in the form of: - * bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent) - * - * Return: 0 success, < 0 for failure - */ -int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period, - u8 *ioss_min_period, u8 *ioss_max_period) -{ - return telm_core_conf.telem_ops->get_sampling_period(pss_min_period, - pss_max_period, - ioss_min_period, - ioss_max_period); -} -EXPORT_SYMBOL_GPL(telemetry_get_sampling_period); - - /** * telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id * @telem_unit: Specify whether IOSS or PSS Read diff --git a/drivers/platform/x86/intel/telemetry/pltdrv.c b/drivers/platform/x86/intel/telemetry/pltdrv.c index 60d3783de7eff..f23c170a55dc6 100644 --- a/drivers/platform/x86/intel/telemetry/pltdrv.c +++ b/drivers/platform/x86/intel/telemetry/pltdrv.c @@ -639,121 +639,6 @@ static int telemetry_setup(struct platform_device *pdev) return 0; } -static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period) -{ - u32 telem_ctrl = 0; - int ret = 0; - - mutex_lock(&(telm_conf->telem_lock)); - if (ioss_period) { - struct intel_scu_ipc_dev *scu = telm_conf->scu; - - if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) { - pr_err("IOSS Sampling Period Out of Range\n"); - ret = -EINVAL; - goto out; - } - - /* Get telemetry EVENT CTL */ - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, - IOSS_TELEM_EVENT_CTL_READ, NULL, 0, - &telem_ctrl, sizeof(telem_ctrl)); - if (ret) { - pr_err("IOSS TELEM_CTRL Read Failed\n"); - goto out; - } - - /* Disable Telemetry */ - TELEM_DISABLE(telem_ctrl); - - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, - IOSS_TELEM_EVENT_CTL_WRITE, - &telem_ctrl, sizeof(telem_ctrl), - NULL, 0); - if (ret) { - pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); - goto out; - } - - /* Enable Periodic Telemetry Events and enable SRAM trace */ - TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl); - TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl); - TELEM_ENABLE_PERIODIC(telem_ctrl); - telem_ctrl |= ioss_period; - - ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM, - IOSS_TELEM_EVENT_CTL_WRITE, - &telem_ctrl, sizeof(telem_ctrl), - NULL, 0); - if (ret) { - pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); - goto out; - } - telm_conf->ioss_config.curr_period = ioss_period; - } - - if (pss_period) { - if (TELEM_SAMPLE_PERIOD_INVALID(pss_period)) { - pr_err("PSS Sampling Period Out of Range\n"); - ret = -EINVAL; - goto out; - } - - /* Get telemetry EVENT CTL */ - ret = intel_punit_ipc_command( - IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL, - 0, 0, NULL, &telem_ctrl); - if (ret) { - pr_err("PSS TELEM_CTRL Read Failed\n"); - goto out; - } - - /* Disable Telemetry */ - TELEM_DISABLE(telem_ctrl); - ret = intel_punit_ipc_command( - IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, - 0, 0, &telem_ctrl, NULL); - if (ret) { - pr_err("PSS TELEM_CTRL Event Disable Write Failed\n"); - goto out; - } - - /* Enable Periodic Telemetry Events and enable SRAM trace */ - TELEM_CLEAR_SAMPLE_PERIOD(telem_ctrl); - TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl); - TELEM_ENABLE_PERIODIC(telem_ctrl); - telem_ctrl |= pss_period; - - ret = intel_punit_ipc_command( - IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL, - 0, 0, &telem_ctrl, NULL); - if (ret) { - pr_err("PSS TELEM_CTRL Event Enable Write Failed\n"); - goto out; - } - telm_conf->pss_config.curr_period = pss_period; - } - -out: - mutex_unlock(&(telm_conf->telem_lock)); - return ret; -} - - -static int telemetry_plt_get_sampling_period(u8 *pss_min_period, - u8 *pss_max_period, - u8 *ioss_min_period, - u8 *ioss_max_period) -{ - *pss_min_period = telm_conf->pss_config.min_period; - *pss_max_period = telm_conf->pss_config.max_period; - *ioss_min_period = telm_conf->ioss_config.min_period; - *ioss_max_period = telm_conf->ioss_config.max_period; - - return 0; -} - - static int telem_evtlog_read(enum telemetry_unit telem_unit, struct telem_ssram_region *ssram_region, u8 len) { @@ -983,8 +868,6 @@ out: static const struct telemetry_core_ops telm_pltops = { .get_trace_verbosity = telemetry_plt_get_trace_verbosity, .set_trace_verbosity = telemetry_plt_set_trace_verbosity, - .set_sampling_period = telemetry_plt_set_sampling_period, - .get_sampling_period = telemetry_plt_get_sampling_period, .raw_read_eventlog = telemetry_plt_raw_read_eventlog, .read_eventlog = telemetry_plt_read_eventlog, }; -- GitLab From 097cd6d6c90cc90410ab03b287166838ea71d41e Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sun, 8 Jun 2025 02:25:12 +0100 Subject: [PATCH 091/868] platform/x86: intel_telemetry: Remove unused telemetry_raw_read_events() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit telemetry_raw_read_events() was added by the commit 378f956e3f93 ("platform/x86: Add Intel Telemetry Core Driver") in 2016 but has remained unused. Remove it. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250608012512.377134-4-linux@treblig.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/include/asm/intel_telemetry.h | 3 --- drivers/platform/x86/intel/telemetry/core.c | 19 ------------------- 2 files changed, 22 deletions(-) diff --git a/arch/x86/include/asm/intel_telemetry.h b/arch/x86/include/asm/intel_telemetry.h index e7fb005ac8d86..944637a4e6dee 100644 --- a/arch/x86/include/asm/intel_telemetry.h +++ b/arch/x86/include/asm/intel_telemetry.h @@ -87,9 +87,6 @@ int telemetry_get_evtname(enum telemetry_unit telem_unit, int telemetry_read_events(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); -int telemetry_raw_read_events(enum telemetry_unit telem_unit, - struct telemetry_evtlog *evtlog, int len); - int telemetry_read_eventlog(enum telemetry_unit telem_unit, struct telemetry_evtlog *evtlog, int len); diff --git a/drivers/platform/x86/intel/telemetry/core.c b/drivers/platform/x86/intel/telemetry/core.c index fe9e8580a8f52..f312864b8d07a 100644 --- a/drivers/platform/x86/intel/telemetry/core.c +++ b/drivers/platform/x86/intel/telemetry/core.c @@ -72,25 +72,6 @@ int telemetry_read_events(enum telemetry_unit telem_unit, } EXPORT_SYMBOL_GPL(telemetry_read_events); -/** - * telemetry_raw_read_events() - Fetch samples specified by evtlog.telem_evt_id - * @telem_unit: Specify whether IOSS or PSS Read - * @evtlog: Array of telemetry_evtlog structs to fill data - * evtlog.telem_evt_id specifies the ids to read - * @len: Length of array of evtlog - * - * The caller must take care of locking in this case. - * - * Return: number of eventlogs read for success, < 0 for failure - */ -int telemetry_raw_read_events(enum telemetry_unit telem_unit, - struct telemetry_evtlog *evtlog, int len) -{ - return telm_core_conf.telem_ops->raw_read_eventlog(telem_unit, evtlog, - len, 0); -} -EXPORT_SYMBOL_GPL(telemetry_raw_read_events); - /** * telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS * @telem_unit: Specify whether IOSS or PSS Read -- GitLab From fe6859aa646b9463379985165f602a44cf25c8ad Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 10 Jun 2025 15:28:24 -0400 Subject: [PATCH 092/868] platform/x86: thinklmi: improved DMI handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix issues reported by kernel test robot. - Require DMI for think-lmi. - Check return from getting serial string Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506062319.F0IpDxF6-lkp@intel.com/ Signed-off-by: Mark Pearson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250610192830.1731454-1-mpearson-lenovo@squebb.ca Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/think-lmi.c | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index e5cbd58a99f3f..9f39547d98f63 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -659,6 +659,7 @@ config THINKPAD_ACPI_HOTKEY_POLL config THINKPAD_LMI tristate "Lenovo WMI-based systems management driver" depends on ACPI_WMI + depends on DMI select FW_ATTR_CLASS help This driver allows changing BIOS settings on Lenovo machines whose diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 00b1e7c79a3d1..02ede1ec99e97 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -772,6 +772,7 @@ static ssize_t certificate_store(struct kobject *kobj, struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); enum cert_install_mode install_mode = TLMI_CERT_INSTALL; char *auth_str, *new_cert; + const char *serial; char *signature; char *guid; int ret; @@ -789,9 +790,10 @@ static ssize_t certificate_store(struct kobject *kobj, return -EACCES; /* Format: 'serial#, signature' */ - auth_str = cert_command(setting, - dmi_get_system_info(DMI_PRODUCT_SERIAL), - setting->signature); + serial = dmi_get_system_info(DMI_PRODUCT_SERIAL); + if (!serial) + return -ENODEV; + auth_str = cert_command(setting, serial, setting->signature); if (!auth_str) return -ENOMEM; -- GitLab From 651b57dd40871d4d0d61fb291e7f26e2b8bd69b1 Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 10 Jun 2025 15:28:25 -0400 Subject: [PATCH 093/868] platform/x86: Move Lenovo files into lenovo subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create lenovo subdirectory for holding Lenovo specific drivers. Signed-off-by: Mark Pearson Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250610192830.1731454-2-mpearson-lenovo@squebb.ca [ij: put depends on DMI back, fix trailing empty lines.] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 16 +- drivers/platform/x86/Kconfig | 230 +---------------- drivers/platform/x86/Makefile | 13 +- drivers/platform/x86/lenovo/Kconfig | 233 ++++++++++++++++++ drivers/platform/x86/lenovo/Makefile | 23 ++ .../x86/{ => lenovo}/ideapad-laptop.c | 0 .../x86/{ => lenovo}/ideapad-laptop.h | 0 drivers/platform/x86/{ => lenovo}/think-lmi.c | 2 +- drivers/platform/x86/{ => lenovo}/think-lmi.h | 0 .../platform/x86/{ => lenovo}/thinkpad_acpi.c | 2 +- .../wmi-camera.c} | 0 .../wmi-hotkey-utilities.c} | 0 .../x86/{lenovo-ymc.c => lenovo/ymc.c} | 0 .../yoga-tab2-pro-1380-fastcharger.c} | 2 +- .../{lenovo-yogabook.c => lenovo/yogabook.c} | 0 15 files changed, 275 insertions(+), 246 deletions(-) create mode 100644 drivers/platform/x86/lenovo/Kconfig create mode 100644 drivers/platform/x86/lenovo/Makefile rename drivers/platform/x86/{ => lenovo}/ideapad-laptop.c (100%) rename drivers/platform/x86/{ => lenovo}/ideapad-laptop.h (100%) rename drivers/platform/x86/{ => lenovo}/think-lmi.c (99%) rename drivers/platform/x86/{ => lenovo}/think-lmi.h (100%) rename drivers/platform/x86/{ => lenovo}/thinkpad_acpi.c (99%) rename drivers/platform/x86/{lenovo-wmi-camera.c => lenovo/wmi-camera.c} (100%) rename drivers/platform/x86/{lenovo-wmi-hotkey-utilities.c => lenovo/wmi-hotkey-utilities.c} (100%) rename drivers/platform/x86/{lenovo-ymc.c => lenovo/ymc.c} (100%) rename drivers/platform/x86/{lenovo-yoga-tab2-pro-1380-fastcharger.c => lenovo/yoga-tab2-pro-1380-fastcharger.c} (99%) rename drivers/platform/x86/{lenovo-yogabook.c => lenovo/yogabook.c} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163..c14614613377d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11612,7 +11612,7 @@ M: Ike Panhc L: platform-driver-x86@vger.kernel.org S: Maintained W: http://launchpad.net/ideapad-laptop -F: drivers/platform/x86/ideapad-laptop.c +F: drivers/platform/x86/lenovo/ideapad-laptop.c IDEAPAD LAPTOP SLIDEBAR DRIVER M: Andrey Moiseev @@ -13671,11 +13671,17 @@ S: Maintained W: http://legousb.sourceforge.net/ F: drivers/usb/misc/legousbtower.c +LENOVO drivers +M: Mark Pearson +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/lenovo/* + LENOVO WMI HOTKEY UTILITIES DRIVER M: Jackie Dong L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/platform/x86/lenovo-wmi-hotkey-utilities.c +F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c LETSKETCH HID TABLET DRIVER M: Hans de Goede @@ -24666,14 +24672,14 @@ S: Maintained W: http://ibm-acpi.sourceforge.net W: http://thinkwiki.org/wiki/Ibm-acpi T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git -F: drivers/platform/x86/thinkpad_acpi.c +F: drivers/platform/x86/lenovo/thinkpad_acpi.c THINKPAD LMI DRIVER -M: Mark Pearson +M: Mark Pearson L: platform-driver-x86@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-class-firmware-attributes -F: drivers/platform/x86/think-lmi.? +F: drivers/platform/x86/lenovo/think-lmi.? THP7312 ISP DRIVER M: Laurent Pinchart diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 9f39547d98f63..43055df448274 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -120,32 +120,6 @@ config GIGABYTE_WMI To compile this driver as a module, choose M here: the module will be called gigabyte-wmi. -config YOGABOOK - tristate "Lenovo Yoga Book tablet key driver" - depends on ACPI_WMI - depends on INPUT - depends on I2C - select LEDS_CLASS - select NEW_LEDS - help - Say Y here if you want to support the 'Pen' key and keyboard backlight - control on the Lenovo Yoga Book tablets. - - To compile this driver as a module, choose M here: the module will - be called lenovo-yogabook. - -config YT2_1380 - tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" - depends on SERIAL_DEV_BUS - depends on EXTCON - depends on ACPI - help - Say Y here to enable support for the custom fast charging protocol - found on the Lenovo Yoga Tablet 2 1380F / 1380L models. - - To compile this driver as a module, choose M here: the module will - be called lenovo-yogabook. - config ACERHDF tristate "Acer Aspire One temperature and fan driver" depends on ACPI_EC && THERMAL @@ -459,43 +433,6 @@ config IBM_RTL state = 0 (BIOS SMIs on) state = 1 (BIOS SMIs off) -config IDEAPAD_LAPTOP - tristate "Lenovo IdeaPad Laptop Extras" - depends on ACPI - depends on RFKILL && INPUT - depends on SERIO_I8042 - depends on BACKLIGHT_CLASS_DEVICE - depends on ACPI_VIDEO || ACPI_VIDEO = n - depends on ACPI_WMI || ACPI_WMI = n - select ACPI_PLATFORM_PROFILE - select INPUT_SPARSEKMAP - select NEW_LEDS - select LEDS_CLASS - help - This is a driver for Lenovo IdeaPad netbooks contains drivers for - rfkill switch, hotkey, fan control and backlight control. - -config LENOVO_WMI_HOTKEY_UTILITIES - tristate "Lenovo Hotkey Utility WMI extras driver" - depends on ACPI_WMI - select NEW_LEDS - select LEDS_CLASS - imply IDEAPAD_LAPTOP - help - This driver provides WMI support for Lenovo customized hotkeys function, - such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin, - Gaming, ThinkBook and so on. - -config LENOVO_YMC - tristate "Lenovo Yoga Tablet Mode Control" - depends on ACPI_WMI - depends on INPUT - depends on IDEAPAD_LAPTOP - select INPUT_SPARSEKMAP - help - This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input - events for Lenovo Yoga notebooks. - config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" depends on INPUT @@ -514,161 +451,8 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. -config THINKPAD_ACPI - tristate "ThinkPad ACPI Laptop Extras" - depends on ACPI_EC - depends on ACPI_BATTERY - depends on INPUT - depends on RFKILL || RFKILL = n - depends on ACPI_VIDEO || ACPI_VIDEO = n - depends on BACKLIGHT_CLASS_DEVICE - depends on I2C - depends on DRM - select ACPI_PLATFORM_PROFILE - select DRM_PRIVACY_SCREEN - select HWMON - select NVRAM - select NEW_LEDS - select LEDS_CLASS - select INPUT_SPARSEKMAP - help - This is a driver for the IBM and Lenovo ThinkPad laptops. It adds - support for Fn-Fx key combinations, Bluetooth control, video - output switching, ThinkLight control, UltraBay eject and more. - For more information about this driver see - and - . - - This driver was formerly known as ibm-acpi. - - Extra functionality will be available if the rfkill (CONFIG_RFKILL) - and/or ALSA (CONFIG_SND) subsystems are available in the kernel. - Note that if you want ThinkPad-ACPI to be built-in instead of - modular, ALSA and rfkill will also have to be built-in. - - If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. - -config THINKPAD_ACPI_ALSA_SUPPORT - bool "Console audio control ALSA interface" - depends on THINKPAD_ACPI - depends on SND - depends on SND = y || THINKPAD_ACPI = SND - default y - help - Enables monitoring of the built-in console audio output control - (headphone and speakers), which is operated by the mute and (in - some ThinkPad models) volume hotkeys. - - If this option is enabled, ThinkPad-ACPI will export an ALSA card - with a single read-only mixer control, which should be used for - on-screen-display feedback purposes by the Desktop Environment. - - Optionally, the driver will also allow software control (the - ALSA mixer will be made read-write). Please refer to the driver - documentation for details. - - All IBM models have both volume and mute control. Newer Lenovo - models only have mute control (the volume hotkeys are just normal - keys and volume control is done through the main HDA mixer). - -config THINKPAD_ACPI_DEBUGFACILITIES - bool "Maintainer debug facilities" - depends on THINKPAD_ACPI - help - Enables extra stuff in the thinkpad-acpi which is completely useless - for normal use. Read the driver source to find out what it does. - - Say N here, unless you were told by a kernel maintainer to do - otherwise. - -config THINKPAD_ACPI_DEBUG - bool "Verbose debug mode" - depends on THINKPAD_ACPI - help - Enables extra debugging information, at the expense of a slightly - increase in driver size. - - If you are not sure, say N here. - -config THINKPAD_ACPI_UNSAFE_LEDS - bool "Allow control of important LEDs (unsafe)" - depends on THINKPAD_ACPI - help - Overriding LED state on ThinkPads can mask important - firmware alerts (like critical battery condition), or misled - the user into damaging the hardware (undocking or ejecting - the bay while buses are still active), etc. - - LED control on the ThinkPad is write-only (with very few - exceptions on very ancient models), which makes it - impossible to know beforehand if important information will - be lost when one changes LED state. - - Users that know what they are doing can enable this option - and the driver will allow control of every LED, including - the ones on the dock stations. - - Never enable this option on a distribution kernel. - - Say N here, unless you are building a kernel for your own - use, and need to control the important firmware LEDs. - -config THINKPAD_ACPI_VIDEO - bool "Video output control support" - depends on THINKPAD_ACPI - default y - help - Allows the thinkpad_acpi driver to provide an interface to control - the various video output ports. - - This feature often won't work well, depending on ThinkPad model, - display state, video output devices in use, whether there is a X - server running, phase of the moon, and the current mood of - Schroedinger's cat. If you can use X.org's RandR to control - your ThinkPad's video output ports instead of this feature, - don't think twice: do it and say N here to save memory and avoid - bad interactions with X.org. - - NOTE: access to this feature is limited to processes with the - CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms - where it interacts badly with X.org. - - If you are not sure, say Y here but do try to check if you could - be using X.org RandR instead. - -config THINKPAD_ACPI_HOTKEY_POLL - bool "Support NVRAM polling for hot keys" - depends on THINKPAD_ACPI - default y - help - Some thinkpad models benefit from NVRAM polling to detect a few of - the hot key press events. If you know your ThinkPad model does not - need to do NVRAM polling to support any of the hot keys you use, - unselecting this option will save about 1kB of memory. - - ThinkPads T40 and newer, R52 and newer, and X31 and newer are - unlikely to need NVRAM polling in their latest BIOS versions. - - NVRAM polling can detect at most the following keys: ThinkPad/Access - IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, - Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). - - If you are not sure, say Y here. The driver enables polling only if - it is strictly necessary to do so. - -config THINKPAD_LMI - tristate "Lenovo WMI-based systems management driver" - depends on ACPI_WMI - depends on DMI - select FW_ATTR_CLASS - help - This driver allows changing BIOS settings on Lenovo machines whose - BIOS support the WMI interface. - - To compile this driver as a module, choose M here: the module will - be called think-lmi. - source "drivers/platform/x86/intel/Kconfig" +source "drivers/platform/x86/lenovo/Kconfig" config ACPI_QUICKSTART tristate "ACPI Quickstart button driver" @@ -1079,18 +863,6 @@ config INSPUR_PLATFORM_PROFILE To compile this driver as a module, choose M here: the module will be called inspur-platform-profile. -config LENOVO_WMI_CAMERA - tristate "Lenovo WMI Camera Button driver" - depends on ACPI_WMI - depends on INPUT - help - This driver provides support for Lenovo camera button. The Camera - button is a GPIO device. This driver receives ACPI notifications when - the camera button is switched on/off. - - To compile this driver as a module, choose M here: the module - will be called lenovo-wmi-camera. - config DASHARO_ACPI tristate "Dasharo ACPI Platform Driver" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index abbc2644ff6db..0530a224bebd5 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -58,17 +58,12 @@ obj-$(CONFIG_X86_PLATFORM_DRIVERS_HP) += hp/ # Hewlett Packard Enterprise obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o -# IBM Thinkpad and Lenovo +# IBM Thinkpad (before 2005) obj-$(CONFIG_IBM_RTL) += ibm_rtl.o -obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o -obj-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += lenovo-wmi-hotkey-utilities.o -obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o -obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o -obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o -obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o -obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o -obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o + +# Lenovo +obj-y += lenovo/ # Intel obj-y += intel/ diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig new file mode 100644 index 0000000000000..e9cb7372a2ca0 --- /dev/null +++ b/drivers/platform/x86/lenovo/Kconfig @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Lenovo X86 Platform Specific Drivers +# + +config IDEAPAD_LAPTOP + tristate "Lenovo IdeaPad Laptop Extras" + depends on ACPI + depends on RFKILL && INPUT + depends on SERIO_I8042 + depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on ACPI_WMI || ACPI_WMI = n + select ACPI_PLATFORM_PROFILE + select INPUT_SPARSEKMAP + select NEW_LEDS + select LEDS_CLASS + help + This is a driver for Lenovo IdeaPad netbooks contains drivers for + rfkill switch, hotkey, fan control and backlight control. + +config LENOVO_WMI_HOTKEY_UTILITIES + tristate "Lenovo Hotkey Utility WMI extras driver" + depends on ACPI_WMI + select NEW_LEDS + select LEDS_CLASS + imply IDEAPAD_LAPTOP + help + This driver provides WMI support for Lenovo customized hotkeys function, + such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin, + Gaming, ThinkBook and so on. + +config LENOVO_WMI_CAMERA + tristate "Lenovo WMI Camera Button driver" + depends on ACPI_WMI + depends on INPUT + help + This driver provides support for Lenovo camera button. The Camera + button is a GPIO device. This driver receives ACPI notifications when + the camera button is switched on/off. + + To compile this driver as a module, choose M here: the module + will be called lenovo-wmi-camera. + +config LENOVO_YMC + tristate "Lenovo Yoga Tablet Mode Control" + depends on ACPI_WMI + depends on INPUT + depends on IDEAPAD_LAPTOP + select INPUT_SPARSEKMAP + help + This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input + events for Lenovo Yoga notebooks. + +config THINKPAD_ACPI + tristate "ThinkPad ACPI Laptop Extras" + depends on ACPI_EC + depends on ACPI_BATTERY + depends on INPUT + depends on RFKILL || RFKILL = n + depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on BACKLIGHT_CLASS_DEVICE + depends on I2C + depends on DRM + select ACPI_PLATFORM_PROFILE + select DRM_PRIVACY_SCREEN + select HWMON + select NVRAM + select NEW_LEDS + select LEDS_CLASS + select INPUT_SPARSEKMAP + help + This is a driver for the IBM and Lenovo ThinkPad laptops. It adds + support for Fn-Fx key combinations, Bluetooth control, video + output switching, ThinkLight control, UltraBay eject and more. + For more information about this driver see + and + . + + This driver was formerly known as ibm-acpi. + + Extra functionality will be available if the rfkill (CONFIG_RFKILL) + and/or ALSA (CONFIG_SND) subsystems are available in the kernel. + Note that if you want ThinkPad-ACPI to be built-in instead of + modular, ALSA and rfkill will also have to be built-in. + + If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. + +config THINKPAD_ACPI_ALSA_SUPPORT + bool "Console audio control ALSA interface" + depends on THINKPAD_ACPI + depends on SND + depends on SND = y || THINKPAD_ACPI = SND + default y + help + Enables monitoring of the built-in console audio output control + (headphone and speakers), which is operated by the mute and (in + some ThinkPad models) volume hotkeys. + + If this option is enabled, ThinkPad-ACPI will export an ALSA card + with a single read-only mixer control, which should be used for + on-screen-display feedback purposes by the Desktop Environment. + + Optionally, the driver will also allow software control (the + ALSA mixer will be made read-write). Please refer to the driver + documentation for details. + + All IBM models have both volume and mute control. Newer Lenovo + models only have mute control (the volume hotkeys are just normal + keys and volume control is done through the main HDA mixer). + +config THINKPAD_ACPI_DEBUGFACILITIES + bool "Maintainer debug facilities" + depends on THINKPAD_ACPI + help + Enables extra stuff in the thinkpad-acpi which is completely useless + for normal use. Read the driver source to find out what it does. + + Say N here, unless you were told by a kernel maintainer to do + otherwise. + +config THINKPAD_ACPI_DEBUG + bool "Verbose debug mode" + depends on THINKPAD_ACPI + help + Enables extra debugging information, at the expense of a slightly + increase in driver size. + + If you are not sure, say N here. + +config THINKPAD_ACPI_UNSAFE_LEDS + bool "Allow control of important LEDs (unsafe)" + depends on THINKPAD_ACPI + help + Overriding LED state on ThinkPads can mask important + firmware alerts (like critical battery condition), or misled + the user into damaging the hardware (undocking or ejecting + the bay while buses are still active), etc. + + LED control on the ThinkPad is write-only (with very few + exceptions on very ancient models), which makes it + impossible to know beforehand if important information will + be lost when one changes LED state. + + Users that know what they are doing can enable this option + and the driver will allow control of every LED, including + the ones on the dock stations. + + Never enable this option on a distribution kernel. + + Say N here, unless you are building a kernel for your own + use, and need to control the important firmware LEDs. + +config THINKPAD_ACPI_VIDEO + bool "Video output control support" + depends on THINKPAD_ACPI + default y + help + Allows the thinkpad_acpi driver to provide an interface to control + the various video output ports. + + This feature often won't work well, depending on ThinkPad model, + display state, video output devices in use, whether there is a X + server running, phase of the moon, and the current mood of + Schroedinger's cat. If you can use X.org's RandR to control + your ThinkPad's video output ports instead of this feature, + don't think twice: do it and say N here to save memory and avoid + bad interactions with X.org. + + NOTE: access to this feature is limited to processes with the + CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms + where it interacts badly with X.org. + + If you are not sure, say Y here but do try to check if you could + be using X.org RandR instead. + +config THINKPAD_ACPI_HOTKEY_POLL + bool "Support NVRAM polling for hot keys" + depends on THINKPAD_ACPI + default y + help + Some thinkpad models benefit from NVRAM polling to detect a few of + the hot key press events. If you know your ThinkPad model does not + need to do NVRAM polling to support any of the hot keys you use, + unselecting this option will save about 1kB of memory. + + ThinkPads T40 and newer, R52 and newer, and X31 and newer are + unlikely to need NVRAM polling in their latest BIOS versions. + + NVRAM polling can detect at most the following keys: ThinkPad/Access + IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, + Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). + + If you are not sure, say Y here. The driver enables polling only if + it is strictly necessary to do so. + +config THINKPAD_LMI + tristate "Lenovo WMI-based systems management driver" + depends on ACPI_WMI + depends on DMI + select FW_ATTR_CLASS + help + This driver allows changing BIOS settings on Lenovo machines whose + BIOS support the WMI interface. + + To compile this driver as a module, choose M here: the module will + be called think-lmi. + +config YOGABOOK + tristate "Lenovo Yoga Book tablet key driver" + depends on ACPI_WMI + depends on INPUT + depends on I2C + select LEDS_CLASS + select NEW_LEDS + help + Say Y here if you want to support the 'Pen' key and keyboard backlight + control on the Lenovo Yoga Book tablets. + + To compile this driver as a module, choose M here: the module will + be called lenovo-yogabook. + +config YT2_1380 + tristate "Lenovo Yoga Tablet 2 1380 fast charge driver" + depends on SERIAL_DEV_BUS + depends on EXTCON + depends on ACPI + help + Say Y here to enable support for the custom fast charging protocol + found on the Lenovo Yoga Tablet 2 1380F / 1380L models. + + To compile this driver as a module, choose M here: the module will + be called lenovo-yogabook. diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile new file mode 100644 index 0000000000000..f1aa2449293b3 --- /dev/null +++ b/drivers/platform/x86/lenovo/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for linux/drivers/platform/x86/lenovo +# Lenovo x86 Platform Specific Drivers +# +obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o +obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o +obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o + +lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += wmi-hotkey-utilities.o +lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o +lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o +lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o +lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o + +# Add 'lenovo' prefix to each module listed in lenovo-target-* +define LENOVO_OBJ_TARGET +lenovo-$(1)-y := $(1).o +obj-$(2) += lenovo-$(1).o +endef + +$(foreach target, $(basename $(lenovo-target-y)), $(eval $(call LENOVO_OBJ_TARGET,$(target),y))) +$(foreach target, $(basename $(lenovo-target-m)), $(eval $(call LENOVO_OBJ_TARGET,$(target),m))) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c similarity index 100% rename from drivers/platform/x86/ideapad-laptop.c rename to drivers/platform/x86/lenovo/ideapad-laptop.c diff --git a/drivers/platform/x86/ideapad-laptop.h b/drivers/platform/x86/lenovo/ideapad-laptop.h similarity index 100% rename from drivers/platform/x86/ideapad-laptop.h rename to drivers/platform/x86/lenovo/ideapad-laptop.h diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c similarity index 99% rename from drivers/platform/x86/think-lmi.c rename to drivers/platform/x86/lenovo/think-lmi.c index 02ede1ec99e97..34a47269e3d34 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/lenovo/think-lmi.c @@ -20,7 +20,7 @@ #include #include #include -#include "firmware_attributes_class.h" +#include "../firmware_attributes_class.h" #include "think-lmi.h" static bool debug_support; diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/lenovo/think-lmi.h similarity index 100% rename from drivers/platform/x86/think-lmi.h rename to drivers/platform/x86/lenovo/think-lmi.h diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c similarity index 99% rename from drivers/platform/x86/thinkpad_acpi.c rename to drivers/platform/x86/lenovo/thinkpad_acpi.c index e7350c9fa3aa6..e1c7bd06fa12f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -81,7 +81,7 @@ #include #include -#include "dual_accel_detect.h" +#include "../dual_accel_detect.h" /* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo/wmi-camera.c similarity index 100% rename from drivers/platform/x86/lenovo-wmi-camera.c rename to drivers/platform/x86/lenovo/wmi-camera.c diff --git a/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c b/drivers/platform/x86/lenovo/wmi-hotkey-utilities.c similarity index 100% rename from drivers/platform/x86/lenovo-wmi-hotkey-utilities.c rename to drivers/platform/x86/lenovo/wmi-hotkey-utilities.c diff --git a/drivers/platform/x86/lenovo-ymc.c b/drivers/platform/x86/lenovo/ymc.c similarity index 100% rename from drivers/platform/x86/lenovo-ymc.c rename to drivers/platform/x86/lenovo/ymc.c diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c similarity index 99% rename from drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c rename to drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c index 25933cd018d17..b3fd6a35052a2 100644 --- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c @@ -21,7 +21,7 @@ #include #include #include -#include "serdev_helpers.h" +#include "../serdev_helpers.h" #define YT2_1380_FC_PDEV_NAME "lenovo-yoga-tab2-pro-1380-fastcharger" #define YT2_1380_FC_SERDEV_CTRL "serial0" diff --git a/drivers/platform/x86/lenovo-yogabook.c b/drivers/platform/x86/lenovo/yogabook.c similarity index 100% rename from drivers/platform/x86/lenovo-yogabook.c rename to drivers/platform/x86/lenovo/yogabook.c -- GitLab From da8f2708f9b69707f4efeb432a18395e46b4666f Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Wed, 14 May 2025 22:10:52 +0200 Subject: [PATCH 094/868] platform/x86: ideapad: Expose charge_types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Ideapad models support a battery conservation mode which limits the battery charge threshold for longer battery longevity. This is currently exposed via a custom conservation_mode attribute in sysfs. The newly introduced charge_types sysfs attribute is a standardized replacement for laptops with a fixed end charge threshold. Setting it to `Long Life` would enable battery conservation mode. The standardized user space API would allow applications such as UPower to detect laptops which support this battery longevity mode and set it. Tested on an Lenovo ideapad U330p. Signed-off-by: Jelle van der Waa Suggested-By: Hans de Goede Reviewed-by: Thomas Weißschuh Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20250514201054.381320-1-jvanderwaa@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../obsolete/sysfs-platform-ideapad-laptop | 8 ++ .../ABI/testing/sysfs-platform-ideapad-laptop | 9 -- drivers/platform/x86/lenovo/Kconfig | 1 + drivers/platform/x86/lenovo/ideapad-laptop.c | 110 +++++++++++++++++- 4 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop diff --git a/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop b/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop new file mode 100644 index 0000000000000..c1dbd19c679c9 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-platform-ideapad-laptop @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/VPC2004:*/conservation_mode +Date: Aug 2017 +KernelVersion: 4.14 +Contact: platform-driver-x86@vger.kernel.org +Description: + Controls whether the conservation mode is enabled or not. + This feature limits the maximum battery charge percentage to + around 50-60% in order to prolong the lifetime of the battery. diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop index 4989ab266682c..5ec0dee9e7074 100644 --- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop @@ -27,15 +27,6 @@ Description: * 1 -> Switched On * 0 -> Switched Off -What: /sys/bus/platform/devices/VPC2004:*/conservation_mode -Date: Aug 2017 -KernelVersion: 4.14 -Contact: platform-driver-x86@vger.kernel.org -Description: - Controls whether the conservation mode is enabled or not. - This feature limits the maximum battery charge percentage to - around 50-60% in order to prolong the lifetime of the battery. - What: /sys/bus/platform/devices/VPC2004:*/fn_lock Date: May 2018 KernelVersion: 4.18 diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index e9cb7372a2ca0..05dab29a22f71 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -6,6 +6,7 @@ config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI + depends on ACPI_BATTERY depends on RFKILL && INPUT depends on SERIO_I8042 depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c index ede483573fe0d..21db9646443e5 100644 --- a/drivers/platform/x86/lenovo/ideapad-laptop.c +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "ideapad-laptop.h" +#include #include #include @@ -162,6 +164,7 @@ struct ideapad_private { struct backlight_device *blightdev; struct ideapad_dytc_priv *dytc; struct dentry *debug; + struct acpi_battery_hook battery_hook; unsigned long cfg; unsigned long r_touchpad_val; struct { @@ -589,6 +592,11 @@ static ssize_t camera_power_store(struct device *dev, static DEVICE_ATTR_RW(camera_power); +static void show_conservation_mode_deprecation_warning(struct device *dev) +{ + dev_warn_once(dev, "conservation_mode attribute has been deprecated, see charge_types.\n"); +} + static ssize_t conservation_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -597,6 +605,8 @@ static ssize_t conservation_mode_show(struct device *dev, unsigned long result; int err; + show_conservation_mode_deprecation_warning(dev); + err = eval_gbmd(priv->adev->handle, &result); if (err) return err; @@ -612,6 +622,8 @@ static ssize_t conservation_mode_store(struct device *dev, bool state; int err; + show_conservation_mode_deprecation_warning(dev); + err = kstrtobool(buf, &state); if (err) return err; @@ -1973,10 +1985,90 @@ static const struct dmi_system_id ctrl_ps2_aux_port_list[] = { {} }; -static void ideapad_check_features(struct ideapad_private *priv) +static int ideapad_psy_ext_set_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct ideapad_private *priv = ext_data; + + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON); + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF); + default: + return -EINVAL; + } +} + +static int ideapad_psy_ext_get_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ideapad_private *priv = ext_data; + unsigned long result; + int err; + + err = eval_gbmd(priv->adev->handle, &result); + if (err) + return err; + + if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result)) + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + else + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + + return 0; +} + +static int ideapad_psy_prop_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp) +{ + return true; +} + +static const enum power_supply_property ideapad_power_supply_props[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const struct power_supply_ext ideapad_battery_ext = { + .name = "ideapad_laptop", + .properties = ideapad_power_supply_props, + .num_properties = ARRAY_SIZE(ideapad_power_supply_props), + .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), + .get_property = ideapad_psy_ext_get_prop, + .set_property = ideapad_psy_ext_set_prop, + .property_is_writeable = ideapad_psy_prop_is_writeable, +}; + +static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook); + + return power_supply_register_extension(battery, &ideapad_battery_ext, + &priv->platform_device->dev, priv); +} + +static int ideapad_battery_remove(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + power_supply_unregister_extension(battery, &ideapad_battery_ext); + + return 0; +} + +static int ideapad_check_features(struct ideapad_private *priv) { acpi_handle handle = priv->adev->handle; unsigned long val; + int err; priv->features.set_fn_lock_led = set_fn_lock_led || dmi_check_system(set_fn_lock_led_list); @@ -1991,8 +2083,16 @@ static void ideapad_check_features(struct ideapad_private *priv) if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) priv->features.fan_mode = true; - if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) + if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { priv->features.conservation_mode = true; + priv->battery_hook.add_battery = ideapad_battery_add; + priv->battery_hook.remove_battery = ideapad_battery_remove; + priv->battery_hook.name = "Ideapad Battery Extension"; + + err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook); + if (err) + return err; + } if (acpi_has_method(handle, "DYTC")) priv->features.dytc = true; @@ -2027,6 +2127,8 @@ static void ideapad_check_features(struct ideapad_private *priv) } } } + + return 0; } #if IS_ENABLED(CONFIG_ACPI_WMI) @@ -2175,7 +2277,9 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (err) return err; - ideapad_check_features(priv); + err = ideapad_check_features(priv); + if (err) + return err; ideapad_debugfs_init(priv); -- GitLab From 55d9fd9819de09e70401b3b5262ff46d5de951b7 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Jun 2025 08:32:06 +0300 Subject: [PATCH 095/868] regulator: bd718x7: Clarify comment by moving it The BD718x7 needs to disable voltage monitoring for a duration of certain voltage changes. The comment explaining use of msleep(1) instead of a more accurate delay(), was placed to a function which disabled the protection. The actual sleeping is done in a different place of the code, after the voltage has been changed. Browsing through the comment and code after the years made me to scratch my head for a second. I may have figured why me and so many fellow developers are slowly getting bald. Clarify things a bit and move the comment about required delay directly above the sleep. Leave only a small comment explaining why the protection is disabled to the spot where the logic for disabling is. Signed-off-by: Matti Vaittinen Link: https://patch.msgid.link/a90cb77e66a253f4055bbb99672dc81c7457de66.1749533040.git.mazziesaccount@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 1bb048de3ecd5..e803cc59d68a5 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -134,9 +134,19 @@ static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel, if (*mask) { /* - * Let's allow scheduling as we use I2C anyways. We just need to - * guarantee minimum of 1ms sleep - it shouldn't matter if we - * exceed it due to the scheduling. + * We had fault detection disabled for the duration of the + * voltage change. + * + * According to HW colleagues the maximum time it takes is + * 1000us. I assume that on systems with light load this + * might be less - and we could probably use DT to give + * system specific delay value if performance matters. + * + * Well, knowing we use I2C here and can add scheduling delays + * I don't think it is worth the hassle and I just add fixed + * 1ms sleep here (and allow scheduling). If this turns out to + * be a problem we can change it to delay and make the delay + * time configurable. */ msleep(1); @@ -173,16 +183,7 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel, /* * If we increase LDO voltage when LDO is enabled we need to * disable the power-good detection until voltage has reached - * the new level. According to HW colleagues the maximum time - * it takes is 1000us. I assume that on systems with light load - * this might be less - and we could probably use DT to give - * system specific delay value if performance matters. - * - * Well, knowing we use I2C here and can add scheduling delays - * I don't think it is worth the hassle and I just add fixed - * 1ms sleep here (and allow scheduling). If this turns out to - * be a problem we can change it to delay and make the delay - * time configurable. + * the new level. */ if (new > now) { int tmp; -- GitLab From a4eb71ff98c4792f441f108910bd829da7a04092 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Jun 2025 00:30:00 +0200 Subject: [PATCH 096/868] regulator: rpi-panel-v2: Fix missing OF dependency Add missing OF dependency and drop of_match_ptr() use. This fixes the following LKP report: " >> drivers/regulator/rpi-panel-v2-regulator.c:95:34: warning: 'rpi_panel_v2_dt_ids' defined but not used [-Wunused-const-variable=] static const struct of_device_id rpi_panel_v2_dt_ids[] = { ^~~~~~~~~~~~~~~~~~~ " Fixes: d49305862fdc ("regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506100440.fyTGO7CG-lkp@intel.com/ Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609223012.87764-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- drivers/regulator/rpi-panel-v2-regulator.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 9a3dc883ff403..7423954153b07 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1156,7 +1156,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" depends on GPIOLIB - depends on I2C + depends on I2C && OF select GPIO_REGMAP select REGMAP_I2C help diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c index b77383584a3a6..c506fd699d572 100644 --- a/drivers/regulator/rpi-panel-v2-regulator.c +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -102,7 +102,7 @@ static struct i2c_driver rpi_panel_v2_regulator_driver = { .driver = { .name = "rpi_touchscreen_v2", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = of_match_ptr(rpi_panel_v2_dt_ids), + .of_match_table = rpi_panel_v2_dt_ids, }, .probe = rpi_panel_v2_i2c_probe, }; -- GitLab From 414145b4cf6c64831aa497f0ec6cc1ca2d76c1d2 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Jun 2025 13:07:46 +0200 Subject: [PATCH 097/868] spi: dt-bindings: mediatek,spi-mt65xx: Add support for MT6991/MT8196 SPI Add support for the SPI IPM controller found on MediaTek's MT6991 (Dimensity) and MT8196 (Kompanio) SoCs, with both having the same controller IP, hence being fully compatible with each other. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250611110747.458090-1-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/mediatek,spi-mt65xx.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml index ed17815263a87..3bf3eb1f87289 100644 --- a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml +++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml @@ -39,6 +39,10 @@ properties: - mediatek,mt7988-spi-single - mediatek,mt8188-spi-ipm - const: mediatek,spi-ipm + - items: + - enum: + - mediatek,mt8196-spi + - const: mediatek,mt6991-spi - items: - enum: - mediatek,mt2701-spi @@ -46,6 +50,7 @@ properties: - mediatek,mt6589-spi - mediatek,mt6765-spi - mediatek,mt6893-spi + - mediatek,mt6991-spi - mediatek,mt7622-spi - mediatek,mt8135-spi - mediatek,mt8173-spi -- GitLab From 6cafcc53eb5fffd9b9bdfde700bb9bad21e98ed3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Jun 2025 13:07:47 +0200 Subject: [PATCH 098/868] spi: spi-mt65xx: Add support for MT6991 Dimensity 9400 SPI IPM Add support for the SPI IPM controller found in the MediaTek Dimensity 9400 (MT6991) SoC. As a note, this is the same SPI IPM Controller IP found on the MT8196 Kompanio counterpart. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250611110747.458090-2-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 4b0a1c0db0414..a6032d44771bf 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -220,6 +220,14 @@ static const struct mtk_spi_compatible mt6893_compat = { .no_need_unprepare = true, }; +static const struct mtk_spi_compatible mt6991_compat = { + .need_pad_sel = true, + .must_tx = true, + .enhance_timing = true, + .dma_ext = true, + .ipm_design = true, +}; + /* * A piece of default chip info unless the platform * supplies it. @@ -245,6 +253,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt6765-spi", .data = (void *)&mt6765_compat, }, + { .compatible = "mediatek,mt6991-spi", + .data = (void *)&mt6991_compat, + }, { .compatible = "mediatek,mt7622-spi", .data = (void *)&mt7622_compat, }, -- GitLab From 9ca30a1b007d5fefb5752428f852a2d8d7219c1c Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 12 Jun 2025 14:02:28 +0800 Subject: [PATCH 099/868] ALSA: usb-audio: Convert comma to semicolon Replace comma between expressions with semicolons. Using a ',' in place of a ';' can have unintended side effects. Although that is not the case here, it is seems best to use ';' unless ',' is intended. Found by inspection. No functional change intended. Compile tested only. Fixes: 79d561c4ec04 ("ALSA: usb-audio: Add mixer quirk for Sony DualSense PS5") Signed-off-by: Chen Ni Reviewed-by: Cristian Ciocaltea Link: https://patch.msgid.link/20250612060228.1518028-1-nichen@iscas.ac.cn Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 0769ecd911440..a0ffe8b559dcc 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -762,9 +762,9 @@ static int snd_dualsense_jack_create(struct usb_mixer_interface *mixer, mei->ih.event = snd_dualsense_ih_event; mei->ih.match = snd_dualsense_ih_match; - mei->ih.connect = snd_dualsense_ih_connect, - mei->ih.disconnect = snd_dualsense_ih_disconnect, - mei->ih.start = snd_dualsense_ih_start, + mei->ih.connect = snd_dualsense_ih_connect; + mei->ih.disconnect = snd_dualsense_ih_disconnect; + mei->ih.start = snd_dualsense_ih_start; mei->ih.name = name; mei->ih.id_table = mei->id_table; -- GitLab From 30f85eddbdebba7665f9b7b772821e588547ca9e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Jun 2025 08:48:01 +0200 Subject: [PATCH 100/868] ALSA: hda/ca0132: Use const char * for strings Some static strings have been stored in fixed char arrays although they could be gracefully "const char *" instead. Also, a few other definitions use the raw "char *" without const. Converting those to "const char *" will save some memory and give more safety, in addition to a side-effect to address the bogus compiler warning with snprintf(). Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506121025.G9uunMlx-lkp@intel.com/ Link: https://patch.msgid.link/20250612064802.1170-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 1904964591dbc..686ce0947131b 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -174,7 +174,7 @@ static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50}; #define DSP_SPEAKER_OUT_LATENCY 7 struct ct_effect { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *name; hda_nid_t nid; int mid; /*effect module ID*/ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/ @@ -305,7 +305,7 @@ enum { }; struct ct_tuning_ctl { - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *name; hda_nid_t parent_nid; hda_nid_t nid; int mid; /*effect module ID*/ @@ -418,14 +418,14 @@ static const struct ct_tuning_ctl ca0132_tuning_ctls[] = { #define VOICEFX_MAX_PARAM_COUNT 9 struct ct_voicefx { - char *name; + const char *name; hda_nid_t nid; int mid; int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/ }; struct ct_voicefx_preset { - char *name; /*preset name*/ + const char *name; /*preset name*/ unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; }; @@ -514,14 +514,14 @@ static const struct ct_voicefx_preset ca0132_voicefx_presets[] = { #define EQ_PRESET_MAX_PARAM_COUNT 11 struct ct_eq { - char *name; + const char *name; hda_nid_t nid; int mid; int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/ }; struct ct_eq_preset { - char *name; /*preset name*/ + const char *name; /*preset name*/ unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT]; }; @@ -679,7 +679,7 @@ enum { }; struct ca0132_alt_speaker_channel_cfg { - char *name; + const char *name; unsigned int val; }; @@ -755,7 +755,7 @@ static const struct ae_ca0113_output_set ae7_ca0113_output_presets = { /* ae5 ca0113 command sequences to set headphone gain levels. */ #define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 struct ae5_headphone_gain_set { - char *name; + const char *name; unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; }; @@ -772,7 +772,7 @@ static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { }; struct ae5_filter_set { - char *name; + const char *name; unsigned int val; }; @@ -5787,7 +5787,7 @@ static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol, static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - char *sfx = "dB"; + const char *sfx = "dB"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -5839,7 +5839,7 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - char *sfx = " Ohms)"; + const char *sfx = " Ohms)"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -- GitLab From 8a5a5cecb79058b608e5562d8998123a3adb313c Mon Sep 17 00:00:00 2001 From: Shenghao Ding Date: Thu, 12 Jun 2025 12:42:52 +0800 Subject: [PATCH 101/868] ASoC: tas2781: Move the "include linux/debugfs.h" into tas2781.h Move the include linux/debugfs.h into tas2781.h for code clean. Signed-off-by: Shenghao Ding Link: https://patch.msgid.link/20250612044252.1025-1-shenghao-ding@ti.com Signed-off-by: Mark Brown --- include/sound/tas2781.h | 4 ++++ sound/soc/codecs/tas2781-i2c.c | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index 40cd3bd079b51..3875e92f1ec5a 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -17,6 +17,10 @@ #ifndef __TAS2781_H__ #define __TAS2781_H__ +#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C +#include +#endif + #include "tas2781-dsp.h" /* version number */ diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index c40d8f754d89a..9f4d965a13351 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -14,9 +14,6 @@ // #include -#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C -#include -#endif #include #include #include -- GitLab From bb90e0c91d375ec5db8a4f8cd2555900aea0725f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:52 +0800 Subject: [PATCH 102/868] ASoC: dt-bindings: Convert MT8173 AFE binding to dt-schema Convert the MT8173 AFE (audio frontend) binding from text to dt-schema in YAML. "clocks" is added to the list of required properties to match "clock-names". And the example was slightly fixed up in style. Otherwise everything is as before. A contributer and maintainer for a recently added MediaTek audio binding was chosen instead of the original submitter. Cc: Trevor Wu Reviewed-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-2-wenst@chromium.org Signed-off-by: Mark Brown --- .../sound/mediatek,mt8173-afe-pcm.yaml | 87 +++++++++++++++++++ .../devicetree/bindings/sound/mtk-afe-pcm.txt | 45 ---------- 2 files changed, 87 insertions(+), 45 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml delete mode 100644 Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml new file mode 100644 index 0000000000000..a95215ba63614 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt8173-afe-pcm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek AFE PCM controller for MT8173 + +maintainers: + - Trevor Wu + +properties: + compatible: + const: mediatek,mt8173-afe-pcm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: audio infra sys clock + - description: audio top mux + - description: audio intbus mux + - description: apll1 clock + - description: apll2 clock + - description: i2s0 mclk mux + - description: i2s1 mclk mux + - description: i2s2 mclk mux + - description: i2s3 mclk mux + - description: i2s3 bclk mux + + clock-names: + items: + - const: infra_sys_audio_clk + - const: top_pdn_audio + - const: top_pdn_aud_intbus + - const: bck0 + - const: bck1 + - const: i2s0_m + - const: i2s1_m + - const: i2s2_m + - const: i2s3_m + - const: i2s3_b + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + + mt8173-afe-pcm@11220000 { + compatible = "mediatek,mt8173-afe-pcm"; + reg = <0x11220000 0x1000>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_AUDIO>, + <&topckgen CLK_TOP_AUDIO_SEL>, + <&topckgen CLK_TOP_AUD_INTBUS_SEL>, + <&topckgen CLK_TOP_APLL1_DIV0>, + <&topckgen CLK_TOP_APLL2_DIV0>, + <&topckgen CLK_TOP_I2S0_M_SEL>, + <&topckgen CLK_TOP_I2S1_M_SEL>, + <&topckgen CLK_TOP_I2S2_M_SEL>, + <&topckgen CLK_TOP_I2S3_M_SEL>, + <&topckgen CLK_TOP_I2S3_B_SEL>; + clock-names = "infra_sys_audio_clk", + "top_pdn_audio", + "top_pdn_aud_intbus", + "bck0", + "bck1", + "i2s0_m", + "i2s1_m", + "i2s2_m", + "i2s3_m", + "i2s3_b"; + }; diff --git a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt deleted file mode 100644 index e302c7f43b959..0000000000000 --- a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt +++ /dev/null @@ -1,45 +0,0 @@ -Mediatek AFE PCM controller - -Required properties: -- compatible = "mediatek,mt8173-afe-pcm"; -- reg: register location and size -- interrupts: Should contain AFE interrupt -- clock-names: should have these clock names: - "infra_sys_audio_clk", - "top_pdn_audio", - "top_pdn_aud_intbus", - "bck0", - "bck1", - "i2s0_m", - "i2s1_m", - "i2s2_m", - "i2s3_m", - "i2s3_b"; - -Example: - - afe: mt8173-afe-pcm@11220000 { - compatible = "mediatek,mt8173-afe-pcm"; - reg = <0 0x11220000 0 0x1000>; - interrupts = ; - clocks = <&infracfg INFRA_AUDIO>, - <&topckgen TOP_AUDIO_SEL>, - <&topckgen TOP_AUD_INTBUS_SEL>, - <&topckgen TOP_APLL1_DIV0>, - <&topckgen TOP_APLL2_DIV0>, - <&topckgen TOP_I2S0_M_CK_SEL>, - <&topckgen TOP_I2S1_M_CK_SEL>, - <&topckgen TOP_I2S2_M_CK_SEL>, - <&topckgen TOP_I2S3_M_CK_SEL>, - <&topckgen TOP_I2S3_B_CK_SEL>; - clock-names = "infra_sys_audio_clk", - "top_pdn_audio", - "top_pdn_aud_intbus", - "bck0", - "bck1", - "i2s0_m", - "i2s1_m", - "i2s2_m", - "i2s3_m", - "i2s3_b"; - }; -- GitLab From 2fd902152c15a8cacab91e4a660413d189411561 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:53 +0800 Subject: [PATCH 103/868] ASoC: dt-bindings: mt8173-afe-pcm: Add power domain The audio subsystem sits under a controllable power domain. Add it to the binding. Acked-by: "Rob Herring (Arm)" Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-3-wenst@chromium.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml index a95215ba63614..666408d32f5c8 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml @@ -45,12 +45,16 @@ properties: - const: i2s3_m - const: i2s3_b + power-domains: + maxItems: 1 + required: - compatible - reg - interrupts - clocks - clock-names + - power-domains additionalProperties: false @@ -59,11 +63,13 @@ examples: #include #include #include + #include mt8173-afe-pcm@11220000 { compatible = "mediatek,mt8173-afe-pcm"; reg = <0x11220000 0x1000>; interrupts = ; + power-domains = <&spm MT8173_POWER_DOMAIN_AUDIO>; clocks = <&infracfg CLK_INFRA_AUDIO>, <&topckgen CLK_TOP_AUDIO_SEL>, <&topckgen CLK_TOP_AUD_INTBUS_SEL>, -- GitLab From 473ee884263f2127ea4e46a74ba15d07446ceabb Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:54 +0800 Subject: [PATCH 104/868] ASoC: dt-bindings: mt8173-afe-pcm: Allow specifying reserved memory region It is desirable to reserve memory for the audio frontend. Allow the "memory-region" property, to be used to point to a reserved memory region. Acked-by: "Rob Herring (Arm)" Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-4-wenst@chromium.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml index 666408d32f5c8..d8993b5d457a9 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml @@ -48,6 +48,10 @@ properties: power-domains: maxItems: 1 + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + required: - compatible - reg @@ -90,4 +94,5 @@ examples: "i2s2_m", "i2s3_m", "i2s3_b"; + memory-region = <&afe_dma_mem>; }; -- GitLab From 81c73294a4eb2df31e974db2fc4397f5e0ecae09 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:55 +0800 Subject: [PATCH 105/868] ASoC: dt-bindings: mt8186-afe-pcm: Allow specifying reserved memory region It is desirable to reserve memory for the audio frontend. Allow the "memory-region" property, to be used to point to a reserved memory region. Acked-by: "Rob Herring (Arm)" Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-5-wenst@chromium.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml index 7fe85b08f9dfa..f5af2cf18158b 100644 --- a/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8186-afe-pcm.yaml @@ -25,6 +25,10 @@ properties: reset-names: const: audiosys + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + mediatek,apmixedsys: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of the mediatek apmixedsys controller @@ -170,6 +174,7 @@ examples: "top_apll12_div_tdm", "top_mux_audio_h", "top_clk26m_clk"; + memory-region = <&afe_dma_mem>; }; ... -- GitLab From cd12d3a5ed10e3e3b323f2b2c652de1c8e17a750 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:56 +0800 Subject: [PATCH 106/868] ASoC: dt-bindings: mt8192-afe-pcm: Allow specifying reserved memory region It is desirable to reserve memory for the audio frontend. Allow the "memory-region" property, to be used to point to a reserved memory region. Acked-by: "Rob Herring (Arm)" Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-6-wenst@chromium.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml index 064ef172bef4c..8ddf49b0040d6 100644 --- a/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml +++ b/Documentation/devicetree/bindings/sound/mt8192-afe-pcm.yaml @@ -23,6 +23,10 @@ properties: reset-names: const: audiosys + memory-region: + description: memory region for audio DMA buffers + maxItems: 1 + mediatek,apmixedsys: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle of the mediatek apmixedsys controller @@ -95,6 +99,7 @@ examples: "aud_dac_predis_clk", "aud_infra_clk", "aud_infra_26m_clk"; + memory-region = <&afe_dma_mem>; }; ... -- GitLab From ec4a10ca4a68ec97f12f4d17d7abb74db34987db Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:57 +0800 Subject: [PATCH 107/868] ASoC: mediatek: use reserved memory or enable buffer pre-allocation In commit 32c9c06adb5b ("ASoC: mediatek: disable buffer pre-allocation") buffer pre-allocation was disabled to accommodate newer platforms that have a limited reserved memory region for the audio frontend. Turns out disabling pre-allocation across the board impacts platforms that don't have this reserved memory region. Buffer allocation failures have been observed on MT8173 and MT8183 based Chromebooks under low memory conditions, which results in no audio playback for the user. Since some MediaTek platforms already have dedicated reserved memory pools for the audio frontend, the plan is to enable this for all of them. This requires device tree changes. As a fallback, reinstate the original policy of pre-allocating audio buffers at probe time of the reserved memory pool cannot be found or used. This patch covers the MT8173, MT8183, MT8186 and MT8192 platforms for now, the reason being that existing MediaTek platform drivers that supported reserved memory were all platforms that mainly supported ChromeOS, and is also the set of devices that I can verify. Fixes: 32c9c06adb5b ("ASoC: mediatek: disable buffer pre-allocation") Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-7-wenst@chromium.org Signed-off-by: Mark Brown --- sound/soc/mediatek/common/mtk-afe-platform-driver.c | 4 +++- sound/soc/mediatek/common/mtk-base-afe.h | 1 + sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 7 +++++++ sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 7 +++++++ sound/soc/mediatek/mt8186/mt8186-afe-pcm.c | 7 +++++++ sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 7 +++++++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 6b63305839414..70fd05d5ff486 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -120,7 +120,9 @@ int mtk_afe_pcm_new(struct snd_soc_component *component, struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); size = afe->mtk_afe_hardware->buffer_bytes_max; - snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev, 0, size); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev, + afe->preallocate_buffers ? size : 0, + size); return 0; } diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index f51578b6c50a3..a406f2e3e7a87 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -117,6 +117,7 @@ struct mtk_base_afe { struct mtk_base_afe_irq *irqs; int irqs_size; int memif_32bit_supported; + bool preallocate_buffers; struct list_head sub_dais; struct snd_soc_dai_driver *dai_drivers; diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 04ed0cfec1741..f93d6348fdf89 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1070,6 +1071,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; + ret = of_reserved_mem_device_init(&pdev->dev); + if (ret) { + dev_info(&pdev->dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + irq_id = platform_get_irq(pdev, 0); if (irq_id <= 0) return irq_id < 0 ? irq_id : -ENXIO; diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index e8884354995cb..9b6b45c646e69 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -777,6 +778,12 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = &pdev->dev; dev = afe->dev; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + /* initial audio related clock */ ret = mt8183_init_clock(afe); if (ret) { diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index db7c93401bee6..c73b4664e53e1 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -2835,6 +2836,12 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev) afe_priv = afe->platform_priv; afe->dev = &pdev->dev; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index fd6af74d79957..3d32fe46118ec 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -2179,6 +2180,12 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) afe->dev = dev; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); + afe->preallocate_buffers = true; + } + /* init audio related clock */ ret = mt8192_init_clock(afe); if (ret) { -- GitLab From 9e7bc5cb8d089d9799e17a9ac99c5da9b13b02e3 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:58 +0800 Subject: [PATCH 108/868] ASoC: mediatek: mt8183-afe-pcm: Support >32 bit DMA addresses The AFE DMA hardware supports up to 34 bits for DMA addresses. This is missing from the driver and prevents reserved memory regions from working properly when the allocated region is above the 4GB line. Fill in the related register offsets for each DAI, and also set the DMA mask. Also fill in the LSB end register offsets for completeness. Fixes: a94aec035a12 ("ASoC: mediatek: mt8183: add platform driver") Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-8-wenst@chromium.org Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 9b6b45c646e69..7383184097a48 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -6,6 +6,7 @@ // Author: KaiChieh Chuang #include +#include #include #include #include @@ -432,6 +433,9 @@ static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = { .reg_ofs_base = AFE_##_id##_BASE, \ .reg_ofs_cur = AFE_##_id##_CUR, \ .reg_ofs_end = AFE_##_id##_END, \ + .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \ + .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \ + .reg_ofs_end_msb = AFE_##_id##_END_MSB, \ .fs_reg = (_fs_reg), \ .fs_shift = _id##_MODE_SFT, \ .fs_maskbit = _id##_MODE_MASK, \ @@ -463,11 +467,17 @@ static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = { #define AFE_VUL12_BASE AFE_VUL_D2_BASE #define AFE_VUL12_CUR AFE_VUL_D2_CUR #define AFE_VUL12_END AFE_VUL_D2_END +#define AFE_VUL12_BASE_MSB AFE_VUL_D2_BASE_MSB +#define AFE_VUL12_CUR_MSB AFE_VUL_D2_CUR_MSB +#define AFE_VUL12_END_MSB AFE_VUL_D2_END_MSB #define AWB2_HD_ALIGN_SFT AWB2_ALIGN_SFT #define VUL12_DATA_SFT VUL12_MONO_SFT #define AFE_HDMI_BASE AFE_HDMI_OUT_BASE #define AFE_HDMI_CUR AFE_HDMI_OUT_CUR #define AFE_HDMI_END AFE_HDMI_OUT_END +#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB +#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB +#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = { MT8183_MEMIF(DL1, AFE_DAC_CON1, AFE_DAC_CON1), @@ -764,6 +774,10 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) struct reset_control *rstc; int i, irq_id, ret; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + if (ret) + return ret; + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; -- GitLab From b2c090c9f6aa9d19f4c966233d7fcb872255f83b Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:48:59 +0800 Subject: [PATCH 109/868] ASoC: mediatek: mt8173-afe-pcm: use local `dev` pointer in driver callbacks The probe and remove functions in the mt8183-afe-pcm driver repeatedly uses `&pdev->dev` for |struct device *|, but then assigns this value to `afe->dev` and uses that in other places in the same function. Store `&pdev->dev` in a local pointer and use that exclusively to avoid the numerous dereferences and to make the code more consistent. Lines are reflowed where it makes sense. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-9-wenst@chromium.org Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 63 +++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index f93d6348fdf89..c0fa623e0b173 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1054,26 +1054,26 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_base_afe *afe; struct mt8173_afe_private *afe_priv; struct snd_soc_component *comp_pcm, *comp_hdmi; + struct device *dev = &pdev->dev; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) return ret; - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; - afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), - GFP_KERNEL); + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL); afe_priv = afe->platform_priv; if (!afe_priv) return -ENOMEM; - afe->dev = &pdev->dev; + afe->dev = dev; - ret = of_reserved_mem_device_init(&pdev->dev); + ret = of_reserved_mem_device_init(dev); if (ret) { - dev_info(&pdev->dev, "no reserved memory found, pre-allocating buffers instead\n"); + dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n"); afe->preallocate_buffers = true; } @@ -1085,27 +1085,27 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); - afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, - &mt8173_afe_regmap_config); + afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr, + &mt8173_afe_regmap_config); if (IS_ERR(afe->regmap)) return PTR_ERR(afe->regmap); /* initial audio related clock */ ret = mt8173_afe_init_audio_clk(afe); if (ret) { - dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n"); + dev_err(dev, "mt8173_afe_init_audio_clk fail\n"); return ret; } /* memif % irq initialize*/ afe->memif_size = MT8173_AFE_MEMIF_NUM; - afe->memif = devm_kcalloc(afe->dev, afe->memif_size, + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), GFP_KERNEL); if (!afe->memif) return -ENOMEM; afe->irqs_size = MT8173_AFE_IRQ_NUM; - afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size, + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), GFP_KERNEL); if (!afe->irqs) return -ENOMEM; @@ -1124,9 +1124,9 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) platform_set_drvdata(pdev, afe); - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = mt8173_afe_runtime_resume(&pdev->dev); + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = mt8173_afe_runtime_resume(dev); if (ret) goto err_pm_disable; } @@ -1136,13 +1136,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) afe->runtime_resume = mt8173_afe_runtime_resume; afe->runtime_suspend = mt8173_afe_runtime_suspend; - ret = devm_snd_soc_register_component(&pdev->dev, - &mtk_afe_pcm_platform, - NULL, 0); + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, + NULL, 0); if (ret) goto err_pm_disable; - comp_pcm = devm_kzalloc(&pdev->dev, sizeof(*comp_pcm), GFP_KERNEL); + comp_pcm = devm_kzalloc(dev, sizeof(*comp_pcm), GFP_KERNEL); if (!comp_pcm) { ret = -ENOMEM; goto err_pm_disable; @@ -1150,7 +1149,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ret = snd_soc_component_initialize(comp_pcm, &mt8173_afe_pcm_dai_component, - &pdev->dev); + dev); if (ret) goto err_pm_disable; @@ -1164,7 +1163,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_pm_disable; - comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL); + comp_hdmi = devm_kzalloc(dev, sizeof(*comp_hdmi), GFP_KERNEL); if (!comp_hdmi) { ret = -ENOMEM; goto err_cleanup_components; @@ -1172,7 +1171,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) ret = snd_soc_component_initialize(comp_hdmi, &mt8173_afe_hdmi_dai_component, - &pdev->dev); + dev); if (ret) goto err_cleanup_components; @@ -1186,30 +1185,32 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_cleanup_components; - ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, + ret = devm_request_irq(dev, irq_id, mt8173_afe_irq_handler, 0, "Afe_ISR_Handle", (void *)afe); if (ret) { - dev_err(afe->dev, "could not request_irq\n"); + dev_err(dev, "could not request_irq\n"); goto err_cleanup_components; } - dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); + dev_info(dev, "MT8173 AFE driver initialized.\n"); return 0; err_cleanup_components: - snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_component(dev); err_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } static void mt8173_afe_pcm_dev_remove(struct platform_device *pdev) { - snd_soc_unregister_component(&pdev->dev); + struct device *dev = &pdev->dev; - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - mt8173_afe_runtime_suspend(&pdev->dev); + snd_soc_unregister_component(dev); + + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt8173_afe_runtime_suspend(dev); } static const struct of_device_id mt8173_afe_pcm_dt_match[] = { -- GitLab From bb8d8ba4715cb8f997d63d90ba935f6073595df5 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 12 Jun 2025 15:49:00 +0800 Subject: [PATCH 110/868] ASoC: mediatek: mt8183-afe-pcm: use local `dev` pointer in driver callbacks The probe and remove functions in the mt8183-afe-pcm driver repeatedly uses `&pdev->dev` for |struct device *|, but then assigns this value to `afe->dev` and uses that in other places in the same function. Store `&pdev->dev` in a local pointer and use that exclusively to avoid the numerous dereferences and to make the code more consistent. Lines are reflowed where it makes sense. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Chen-Yu Tsai Link: https://patch.msgid.link/20250612074901.4023253-10-wenst@chromium.org Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 7383184097a48..a7fef772760a6 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -770,27 +770,25 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8183_afe_private *afe_priv; - struct device *dev; + struct device *dev = &pdev->dev; struct reset_control *rstc; int i, irq_id, ret; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34)); if (ret) return ret; - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; platform_set_drvdata(pdev, afe); - afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), - GFP_KERNEL); + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL); if (!afe->platform_priv) return -ENOMEM; afe_priv = afe->platform_priv; - afe->dev = &pdev->dev; - dev = afe->dev; + afe->dev = dev; ret = of_reserved_mem_device_init(dev); if (ret) { @@ -835,7 +833,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_sync(dev); ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config); if (ret) { @@ -843,7 +841,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) goto err_pm_disable; } - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; regcache_cache_only(afe->regmap, true); @@ -901,7 +899,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { ret = dai_register_cbs[i](afe); if (ret) { - dev_warn(afe->dev, "dai register i %d fail, ret %d\n", + dev_warn(dev, "dai register i %d fail, ret %d\n", i, ret); goto err_pm_disable; } @@ -910,8 +908,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) /* init dai_driver and component_driver */ ret = mtk_afe_combine_sub_dai(afe); if (ret) { - dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n", - ret); + dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n", ret); goto err_pm_disable; } @@ -923,16 +920,14 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) afe->runtime_suspend = mt8183_afe_runtime_suspend; /* register component */ - ret = devm_snd_soc_register_component(&pdev->dev, - &mtk_afe_pcm_platform, + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, NULL, 0); if (ret) { dev_warn(dev, "err_platform\n"); goto err_pm_disable; } - ret = devm_snd_soc_register_component(afe->dev, - &mt8183_afe_pcm_dai_component, + ret = devm_snd_soc_register_component(dev, &mt8183_afe_pcm_dai_component, afe->dai_drivers, afe->num_dai_drivers); if (ret) { @@ -943,15 +938,17 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev) return ret; err_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); return ret; } static void mt8183_afe_pcm_dev_remove(struct platform_device *pdev) { - pm_runtime_disable(&pdev->dev); - if (!pm_runtime_status_suspended(&pdev->dev)) - mt8183_afe_runtime_suspend(&pdev->dev); + struct device *dev = &pdev->dev; + + pm_runtime_disable(dev); + if (!pm_runtime_status_suspended(dev)) + mt8183_afe_runtime_suspend(dev); } static const struct of_device_id mt8183_afe_pcm_dt_match[] = { -- GitLab From dfce24f0032439113848939816ef78b6e83f4086 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Thu, 12 Jun 2025 17:00:22 +0100 Subject: [PATCH 111/868] ALSA: hda: cs35l41: Add support for center channel in CS35L41 HDA Currently only left and right channels are supported for each amp. Support is needed for a center channel, using both left and right channel audio. Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20250612160029.848104-2-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- include/sound/cs35l41.h | 12 +++++ sound/pci/hda/cs35l41_hda.c | 103 +++++++++++++++++++++++------------- sound/pci/hda/cs35l41_hda.h | 1 + 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 43c6a9ef8d9f1..7542cabfa7266 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -609,6 +609,18 @@ #define CS35L41_DSP_NG_DELAY_MASK 0x0F00 #define CS35L41_DSP_NG_DELAY_SHIFT 8 +#define CS35L41_ASP_RX1_EN_MASK 0x00010000 +#define CS35L41_ASP_RX1_EN_SHIFT 16 +#define CS35L41_ASP_RX2_EN_MASK 0x00020000 +#define CS35L41_ASP_RX2_EN_SHIFT 17 +#define CS35L41_ASP_TX1_EN_MASK 0x00000001 +#define CS35L41_ASP_TX1_EN_SHIFT 0 +#define CS35L41_ASP_TX2_EN_MASK 0x00000002 +#define CS35L41_ASP_TX2_EN_SHIFT 1 +#define CS35L41_ASP_TX3_EN_MASK 0x00000004 +#define CS35L41_ASP_TX3_EN_SHIFT 2 +#define CS35L41_ASP_TX4_EN_MASK 0x00000008 +#define CS35L41_ASP_TX4_EN_SHIFT 3 #define CS35L41_ASP_FMT_MASK 0x0700 #define CS35L41_ASP_FMT_SHIFT 8 #define CS35L41_ASP_DOUT_HIZ_MASK 0x03 diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index d5bc81099d0d6..c2cf3813872a0 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -93,47 +93,36 @@ module_param(firmware_autostart, bool, 0444); MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot" "(0=Disable, 1=Enable) (default=1); "); +static const char channel_name[3] = { 'L', 'R', 'C' }; + static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010000 }, // ASP_RX1_EN = 1 { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer - { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON - { CS35L41_ASP_TX3_SRC, 0x00000032 }, // ASPTX3 SRC = ERRVOL - { CS35L41_ASP_TX4_SRC, 0x00000033 }, // ASPTX4 SRC = CLASSH_TGT - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000009 }, // DSP1RX2 SRC = ASPRX2 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON +}; + +static const struct reg_sequence cs35l41_hda_config_no_dsp[] = { + { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1 + { CS35L41_ASP_TX3_SRC, 0x00000000 }, // ASPTX3 SRC = ZERO FILL + { CS35L41_ASP_TX4_SRC, 0x00000000 }, // ASPTX4 SRC = ZERO FILL { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL + { CS35L41_DSP1_RX6_SRC, 0x00000021 }, // DSP1RX6 SRC = CLASSH_TGT }; static const struct reg_sequence cs35l41_hda_config_dsp[] = { - { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1 - { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN - { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz - { CS35L41_SP_ENABLES, 0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1 - { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz - { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled - { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 - { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON - { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON - { CS35L41_DSP1_RX1_SRC, 0x00000008 }, // DSP1RX1 SRC = ASPRX1 - { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 - { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON - { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; @@ -657,6 +646,41 @@ static void cs35l41_irq_release(struct cs35l41_hda *cs35l41) cs35l41->irq_errors = 0; } +static void cs35l41_update_mixer(struct cs35l41_hda *cs35l41) +{ + struct regmap *reg = cs35l41->regmap; + unsigned int asp_en = 0; + unsigned int dsp1rx2_src = 0; + + regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); + + if (cs35l41->cs_dsp.running) { + asp_en |= CS35L41_ASP_TX1_EN_MASK; // ASP_TX1_EN = 1 + regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, + ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_config_no_dsp, + ARRAY_SIZE(cs35l41_hda_config_no_dsp)); + } + + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) { + asp_en |= CS35L41_ASP_RX2_EN_MASK; // ASP_RX2_EN = 1 + dsp1rx2_src = 0x00000009; // DSP1RX2 SRC = ASPRX2 + } else { + dsp1rx2_src = 0x00000008; // DSP1RX2 SRC = ASPRX1 + } + + asp_en |= CS35L41_ASP_RX1_EN_MASK; // ASP_RX1_EN = 1 + + regmap_write(reg, CS35L41_SP_ENABLES, asp_en); + regmap_write(reg, CS35L41_DSP1_RX1_SRC, 0x00000008); // DSP1RX1 SRC = ASPRX1 + regmap_write(reg, CS35L41_DSP1_RX2_SRC, dsp1rx2_src); +} + static void cs35l41_hda_play_start(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); @@ -671,19 +695,13 @@ static void cs35l41_hda_play_start(struct device *dev) cs35l41->playback_started = true; + cs35l41_update_mixer(cs35l41); + if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, - ARRAY_SIZE(cs35l41_hda_config_dsp)); - if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); - else - regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); } regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) @@ -841,22 +859,31 @@ static void cs35l41_hda_post_playback_hook(struct device *dev, int action) } } -static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) +static int cs35l41_hda_channel_map(struct cs35l41_hda *cs35l41) { - struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); - static const char * const channel_name[] = { "L", "R" }; + unsigned int tx_num = 0; + unsigned int *tx_slot = NULL; + unsigned int rx_num; + unsigned int *rx_slot; + unsigned int mono = 0; if (!cs35l41->amp_name) { - if (*rx_slot >= ARRAY_SIZE(channel_name)) + if (cs35l41->hw_cfg.spk_pos >= ARRAY_SIZE(channel_name)) return -EINVAL; - cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d", - channel_name[*rx_slot], cs35l41->channel_index); + cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%c%d", + channel_name[cs35l41->hw_cfg.spk_pos], + cs35l41->channel_index); if (!cs35l41->amp_name) return -ENOMEM; } + rx_num = 1; + if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) + rx_slot = &mono; + else + rx_slot = &cs35l41->hw_cfg.spk_pos; + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num, rx_slot); } @@ -1495,7 +1522,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH, - cs35l41->hw_cfg.spk_pos ? 'R' : 'L', + channel_name[cs35l41->hw_cfg.spk_pos], cs35l41->cs_dsp.running, cs35l41->speaker_id); return ret; @@ -1709,7 +1736,7 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) if (using_irq) cs35l41_configure_interrupt(cs35l41, irq_pol); - return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); + return cs35l41_hda_channel_map(cs35l41); } int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id) diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index c730b33515894..7d003c598e93e 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -42,6 +42,7 @@ struct cs35l41_amp_efi_data { enum cs35l41_hda_spk_pos { CS35L41_LEFT, CS35L41_RIGHT, + CS35L41_CENTER, }; enum cs35l41_hda_gpio_function { -- GitLab From 84fc8896f0d9d1c075e0e08a416faedbd73907fa Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Thu, 12 Jun 2025 17:00:23 +0100 Subject: [PATCH 112/868] ALSA: hda/realtek: Add support for ASUS NUC using CS35L41 HDA Add support for ASUS NUC14LNS. This NUC uses a single CS35L41 Amp in using Internal Boost with SPI. To support the Single Amp, a new quirk is required. Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20250612160029.848104-3-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cd0d7ba7320ef..7db62477b7857 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7296,6 +7296,11 @@ static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixu comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } +static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1); +} + static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); @@ -7986,6 +7991,7 @@ enum { ALC287_FIXUP_CS35L41_I2C_2, ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, ALC287_FIXUP_CS35L41_I2C_4, + ALC245_FIXUP_CS35L41_SPI_1, ALC245_FIXUP_CS35L41_SPI_2, ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED, ALC245_FIXUP_CS35L41_SPI_4, @@ -10112,6 +10118,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_spi_two, }, + [ALC245_FIXUP_CS35L41_SPI_1] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_one, + }, [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, .v.func = cs35l41_fixup_spi_two, @@ -11049,6 +11059,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), + SND_PCI_QUIRK(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), -- GitLab From 6418a8504187dc7f5b6f9d0649c03e362cb0664b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 29 May 2025 11:18:37 -0700 Subject: [PATCH 113/868] platform/x86: thinkpad_acpi: Handle KCOV __init vs inline mismatches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When KCOV is enabled all functions get instrumented, unless the __no_sanitize_coverage attribute is used. To prepare for __no_sanitize_coverage being applied to __init functions[1], we have to handle differences in how GCC's inline optimizations get resolved. For thinkpad_acpi routines, this means forcing two functions to be inline with __always_inline. Link: https://lore.kernel.org/lkml/20250523043935.2009972-11-kees@kernel.org/ [1] Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20250529181831.work.439-kees@kernel.org Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index e1c7bd06fa12f..d5bfaa3d0de59 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -559,12 +559,12 @@ static unsigned long __init tpacpi_check_quirks( return 0; } -static inline bool __pure __init tpacpi_is_lenovo(void) +static __always_inline bool __pure __init tpacpi_is_lenovo(void) { return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO; } -static inline bool __pure __init tpacpi_is_ibm(void) +static __always_inline bool __pure __init tpacpi_is_ibm(void) { return thinkpad_id.vendor == PCI_VENDOR_ID_IBM; } -- GitLab From 05651018f04ab00a84da8a47feb3a605ec1c7e41 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Mon, 9 Jun 2025 14:34:49 +0200 Subject: [PATCH 114/868] platform/x86: lenovo-yoga-tab2-pro-1380-fastcharger: Use devm_pinctrl_register_mappings() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_pinctrl_register_mappings(), so the core automatically unregisters the pinctrl mappings. It makes the code easier to read. Signed-off-by: Thomas Richard Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250609-lenovo-yoga-tab2-pro-1380-fastcharger-devm-pinctrl-register-mappings-v1-1-fb601f2b80f6@bootlin.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../lenovo/yoga-tab2-pro-1380-fastcharger.c | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c index b3fd6a35052a2..1b33c977f6d76 100644 --- a/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo/yoga-tab2-pro-1380-fastcharger.c @@ -240,30 +240,25 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev) int ret; /* Register pinctrl mappings for setting the UART3 pins mode */ - ret = pinctrl_register_mappings(yt2_1380_fc_pinctrl_map, - ARRAY_SIZE(yt2_1380_fc_pinctrl_map)); + ret = devm_pinctrl_register_mappings(&pdev->dev, yt2_1380_fc_pinctrl_map, + ARRAY_SIZE(yt2_1380_fc_pinctrl_map)); if (ret) return ret; /* And create the serdev to talk to the charger over the UART3 pins */ ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL); - if (IS_ERR(ctrl_dev)) { - ret = PTR_ERR(ctrl_dev); - goto out_pinctrl_unregister_mappings; - } + if (IS_ERR(ctrl_dev)) + return PTR_ERR(ctrl_dev); serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); put_device(ctrl_dev); - if (!serdev) { - ret = -ENOMEM; - goto out_pinctrl_unregister_mappings; - } + if (!serdev) + return -ENOMEM; ret = serdev_device_add(serdev); if (ret) { - dev_err_probe(&pdev->dev, ret, "adding serdev\n"); serdev_device_put(serdev); - goto out_pinctrl_unregister_mappings; + return dev_err_probe(&pdev->dev, ret, "adding serdev\n"); } /* @@ -273,20 +268,15 @@ static int yt2_1380_fc_pdev_probe(struct platform_device *pdev) ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev); if (ret) { /* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */ - ret = (ret == -EAGAIN) ? -EPROBE_DEFER : ret; - dev_err_probe(&pdev->dev, ret, "attaching serdev driver\n"); - goto out_serdev_device_remove; + serdev_device_remove(serdev); + return dev_err_probe(&pdev->dev, + (ret == -EAGAIN) ? -EPROBE_DEFER : ret, + "attaching serdev driver\n"); } /* So that yt2_1380_fc_pdev_remove() can remove the serdev */ platform_set_drvdata(pdev, serdev); return 0; - -out_serdev_device_remove: - serdev_device_remove(serdev); -out_pinctrl_unregister_mappings: - pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map); - return ret; } static void yt2_1380_fc_pdev_remove(struct platform_device *pdev) @@ -294,7 +284,6 @@ static void yt2_1380_fc_pdev_remove(struct platform_device *pdev) struct serdev_device *serdev = platform_get_drvdata(pdev); serdev_device_remove(serdev); - pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map); } static struct platform_driver yt2_1380_fc_pdev_driver = { -- GitLab From 5a7c909a53875e9c0c64cdf8e52b5716d8a74523 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 12 Jun 2025 09:48:35 +0200 Subject: [PATCH 115/868] platform/x86: silicom: remove unnecessary GPIO line direction check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of commit 92ac7de3175e3 ("gpiolib: don't allow setting values on input lines"), the GPIO core makes sure values cannot be set on input lines. Remove the unnecessary check. Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20250612074835.13800-1-brgl@bgdev.pl Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/silicom-platform.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c index 021f3fed197a6..63b5da410ed57 100644 --- a/drivers/platform/x86/silicom-platform.c +++ b/drivers/platform/x86/silicom-platform.c @@ -248,13 +248,9 @@ static int silicom_gpio_direction_input(struct gpio_chip *gc, static int silicom_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { - int direction = silicom_gpio_get_direction(gc, offset); u8 *channels = gpiochip_get_data(gc); int channel = channels[offset]; - if (direction == GPIO_LINE_DIRECTION_IN) - return -EPERM; - silicom_mec_port_set(channel, !value); return 0; -- GitLab From d9926f09edabd81db86768fd87eff3e163f109e2 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Mon, 9 Jun 2025 12:21:13 +0200 Subject: [PATCH 116/868] platform/x86: fujitsu: use unsigned int for kstrtounit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The charge control threshold value ranges from 0-100. Signed-off-by: Jelle van der Waa Reviewed-by: Hans de Goede Acked-by: Jonathan Woithe Link: https://lore.kernel.org/r/20250609102115.36936-2-jvanderwaa@redhat.com [ij: use reverse xmas-tree order] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 162809140f68a..c6ec61518160b 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -180,7 +180,8 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, const char *buf, size_t count) { int cc_end_value, s006_cc_return; - int value, ret; + unsigned int value; + int ret; ret = kstrtouint(buf, 10, &value); if (ret) -- GitLab From dce77641056ea4fb2efab0a66d6929a5008d7235 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Mon, 9 Jun 2025 12:21:14 +0200 Subject: [PATCH 117/868] platform/x86: fujitsu: clamp charge_control_end_threshold values to 50 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the sysfs ABI documentation that drivers should round written values to the nearest supported value instead of returning an error. Tested on a Fujitsu Lifebook U7720. Signed-off-by: Jelle van der Waa Reviewed-by: Hans de Goede Acked-by: Jonathan Woithe Link: https://lore.kernel.org/r/20250609102115.36936-3-jvanderwaa@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index c6ec61518160b..931fbcdd21b88 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -187,9 +187,12 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, if (ret) return ret; - if (value < 50 || value > 100) + if (value > 100) return -EINVAL; + if (value < 50) + value = 50; + cc_end_value = value * 0x100 + 0x20; s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD, CHARGE_CONTROL_RW, cc_end_value, 0x0); -- GitLab From d2b16853ad704ac7e1550d6faacdc53925494ebf Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 10 Jun 2025 10:41:07 +0200 Subject: [PATCH 118/868] platform: arm64: lenovo-yoga-c630: use the auxiliary device creation helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auxiliary device creation of this driver is simple enough to use the available auxiliary device creation helper. Use it and remove some boilerplate code. Signed-off-by: Jerome Brunet Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20250610-yoga-aux-v1-1-d6115aa1683c@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/arm64/lenovo-yoga-c630.c | 40 ++--------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c index 1f05c9a6a89d5..75060c842b249 100644 --- a/drivers/platform/arm64/lenovo-yoga-c630.c +++ b/drivers/platform/arm64/lenovo-yoga-c630.c @@ -191,50 +191,16 @@ void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_blo } EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify); -static void yoga_c630_aux_release(struct device *dev) -{ - struct auxiliary_device *adev = to_auxiliary_dev(dev); - - kfree(adev); -} - -static void yoga_c630_aux_remove(void *data) -{ - struct auxiliary_device *adev = data; - - auxiliary_device_delete(adev); - auxiliary_device_uninit(adev); -} - static int yoga_c630_aux_init(struct device *parent, const char *name, struct yoga_c630_ec *ec) { struct auxiliary_device *adev; - int ret; - adev = kzalloc(sizeof(*adev), GFP_KERNEL); + adev = devm_auxiliary_device_create(parent, name, ec); if (!adev) - return -ENOMEM; - - adev->name = name; - adev->id = 0; - adev->dev.parent = parent; - adev->dev.release = yoga_c630_aux_release; - adev->dev.platform_data = ec; - - ret = auxiliary_device_init(adev); - if (ret) { - kfree(adev); - return ret; - } - - ret = auxiliary_device_add(adev); - if (ret) { - auxiliary_device_uninit(adev); - return ret; - } + return -ENODEV; - return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev); + return 0; } static int yoga_c630_ec_probe(struct i2c_client *client) -- GitLab From c9de2e5c15cba9e9be7fd124a74b9067560d4746 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 10 Jun 2025 07:55:25 +0200 Subject: [PATCH 119/868] Documentation: ABI: Update WMI device paths in ABI docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WMI driver core might append an ID to the WMI device name to avoid name collisions in case multiple WMI devices with the same GUID are present. Update all sysfs path referring to WMI devices to inform users about this important detail. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20250610055526.23688-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../ABI/testing/sysfs-platform-dell-privacy-wmi | 8 ++++---- .../ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update | 2 +- .../ABI/testing/sysfs-platform-intel-wmi-thunderbolt | 2 +- Documentation/admin-guide/thunderbolt.rst | 9 ++------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi b/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi index 1f1f274a6979f..b4da7b2ea0ca0 100644 --- a/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi +++ b/Documentation/ABI/testing/sysfs-platform-dell-privacy-wmi @@ -1,4 +1,4 @@ -What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type +What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_supported_type Date: Apr 2021 KernelVersion: 5.13 Contact: "" @@ -29,12 +29,12 @@ Description: For example to check which privacy devices are supported:: - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_supported_type + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_supported_type [Microphone Mute] [supported] [Camera Shutter] [supported] [ePrivacy Screen] [unsupported] -What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state +What: /sys/bus/wmi/devices/6932965F-1671-4CEB-B988-D3AB0A901919[-X]/dell_privacy_current_state Date: Apr 2021 KernelVersion: 5.13 Contact: "" @@ -66,6 +66,6 @@ Description: For example to check all supported current privacy device states:: - # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919/dell_privacy_current_state + # cat /sys/bus/wmi/drivers/dell-privacy/6932965F-1671-4CEB-B988-D3AB0A901919*/dell_privacy_current_state [Microphone] [unmuted] [Camera Shutter] [unmuted] diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update b/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update index 02ae1e9bbfc8d..7ffd1579b8f72 100644 --- a/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update +++ b/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update @@ -1,4 +1,4 @@ -What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request +What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651[-X]/firmware_update_request Date: April 2020 KernelVersion: 5.7 Contact: "Jithu Joseph" diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt index fd3a7ec79760f..10ef1282c9d2e 100644 --- a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt +++ b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt @@ -1,4 +1,4 @@ -What: /sys/devices/platform//force_power +What: /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341[-X]/force_power Date: September 2017 KernelVersion: 4.15 Contact: "Mario Limonciello" diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst index 240fee618e066..102c693c8f816 100644 --- a/Documentation/admin-guide/thunderbolt.rst +++ b/Documentation/admin-guide/thunderbolt.rst @@ -358,12 +358,7 @@ Forcing power Many OEMs include a method that can be used to force the power of a Thunderbolt controller to an "On" state even if nothing is connected. If supported by your machine this will be exposed by the WMI bus with -a sysfs attribute called "force_power". - -For example the intel-wmi-thunderbolt driver exposes this attribute in: - /sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power - - To force the power to on, write 1 to this attribute file. - To disable force power, write 0 to this attribute file. +a sysfs attribute called "force_power", see +Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt for details. Note: it's currently not possible to query the force power state of a platform. -- GitLab From 73f0f2b52c5ea67b3140b23f58d8079d158839c8 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 10 Jun 2025 07:55:26 +0200 Subject: [PATCH 120/868] platform/x86: wmi: Fix WMI device naming issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple WMI devices with the same GUID are present inside a given system, the WMI driver core might fail to register all of them. Consider the following scenario: WMI devices ([-]): 05901221-D566-11D1-B2F0-00A0C9062910 (on PNP0C14:00) 05901221-D566-11D1-B2F0-00A0C9062910-1 (on PNP0C14:01) If the WMI core driver somehow unbinds from PNP0C14:00, the following will happen upon rebinding: 1. The WMI driver core counts all registered WMI devices with a GUID of 05901221-D566-11D1-B2F0-00A0C9062910 (count: 1). 2. The new WMI device will be named "05901221-D566-11D1-B2F0-00A0C9062910-1" because another device with the same GUID is already registered (on PNP0C14:01). 3. The new WMI device cannot be registered due to a name conflict. Use a IDA when building the WMI device name to avoid such name collisions by ensuring that a given WMI device ID is not reused. Userspace applications using udev for WMI device detection are not impacted by this change. Additionally userspace applications that do fully support the existing naming scheme are also not impacted. Only userspace applications using hardcoded sysfs paths will break. Introduce a kconfig option for restoring the old naming scheme to give developers time to fix any compatibility issues. Tested on a Asus Prime B650-Plus. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20250610055526.23688-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 9 +++++++++ drivers/platform/x86/wmi.c | 37 +++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 43055df448274..49ca98df47fd8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -37,6 +37,15 @@ config ACPI_WMI It is safe to enable this driver even if your DSDT doesn't define any ACPI-WMI devices. +config ACPI_WMI_LEGACY_DEVICE_NAMES + bool "Use legacy WMI device naming scheme" + depends on ACPI_WMI + help + Say Y here to force the WMI driver core to use the old WMI device naming + scheme when creating WMI devices. Doing so might be necessary for some + userspace applications but will cause the registration of WMI devices with + the same GUID to fail in some corner cases. + config WMI_BMOF tristate "WMI embedded Binary MOF driver" depends on ACPI_WMI diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e46453750d5f1..21b7e54bd7ab3 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,8 @@ struct wmi_guid_count_context { int count; }; +static DEFINE_IDA(wmi_ida); + /* * If the GUID data block is marked as expensive, we must enable and * explicitily disable data collection. @@ -978,6 +981,19 @@ static int guid_count(const guid_t *guid) return context.count; } +static int wmi_dev_set_name(struct wmi_block *wblock, int count) +{ + if (IS_ENABLED(CONFIG_ACPI_WMI_LEGACY_DEVICE_NAMES)) { + if (count) + return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, + count); + else + return dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); + } + + return dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, wblock->dev.dev.id); +} + static int wmi_create_device(struct device *wmi_bus_dev, struct wmi_block *wblock, struct acpi_device *device) @@ -986,7 +1002,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, struct acpi_device_info *info; acpi_handle method_handle; acpi_status status; - int count; + int count, ret; if (wblock->gblock.flags & ACPI_WMI_EVENT) { wblock->dev.dev.type = &wmi_type_event; @@ -1057,11 +1073,18 @@ static int wmi_create_device(struct device *wmi_bus_dev, if (count < 0) return count; - if (count) { - dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); + if (count) set_bit(WMI_GUID_DUPLICATED, &wblock->flags); - } else { - dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); + + ret = ida_alloc(&wmi_ida, GFP_KERNEL); + if (ret < 0) + return ret; + + wblock->dev.dev.id = ret; + ret = wmi_dev_set_name(wblock, count); + if (ret < 0) { + ida_free(&wmi_ida, wblock->dev.dev.id); + return ret; } device_initialize(&wblock->dev.dev); @@ -1147,6 +1170,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) dev_err(wmi_bus_dev, "failed to register %pUL\n", &wblock->gblock.guid); + ida_free(&wmi_ida, wblock->dev.dev.id); put_device(&wblock->dev.dev); } } @@ -1246,7 +1270,10 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context static int wmi_remove_device(struct device *dev, void *data) { + int id = dev->id; + device_unregister(dev); + ida_free(&wmi_ida, id); return 0; } -- GitLab From d0630a0b80c08530857146e3bf183a7d6b743847 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 13 Jun 2025 10:15:30 +0200 Subject: [PATCH 121/868] ALSA: usb-audio: Fix build with CONFIG_INPUT=n The recent addition of DualSense mixer quirk relies on the input device handle, and the build can fail if CONFIG_INPUT isn't set. Put (rather ugly) workarounds to wrap with IS_REACHABLE() for avoiding the build error. Fixes: 79d561c4ec04 ("ALSA: usb-audio: Add mixer quirk for Sony DualSense PS5") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506130733.gnPKw2l3-lkp@intel.com/ Reviewed-by: Cristian Ciocaltea Link: https://patch.msgid.link/20250613081543.7404-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index a0ffe8b559dcc..7cc27ae5512f0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -532,6 +532,7 @@ static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) &snd_emu0204_control, NULL); } +#if IS_REACHABLE(CONFIG_INPUT) /* * Sony DualSense controller (PS5) jack detection * @@ -788,6 +789,7 @@ static int snd_dualsense_controls_create(struct usb_mixer_interface *mixer) return snd_dualsense_jack_create(mixer, "Headset Mic Jack", false); } +#endif /* IS_REACHABLE(CONFIG_INPUT) */ /* ASUS Xonar U1 / U3 controls */ @@ -4331,10 +4333,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_emu0204_controls_create(mixer); break; +#if IS_REACHABLE(CONFIG_INPUT) case USB_ID(0x054c, 0x0ce6): /* Sony DualSense controller (PS5) */ case USB_ID(0x054c, 0x0df2): /* Sony DualSense Edge controller (PS5) */ err = snd_dualsense_controls_create(mixer); break; +#endif /* IS_REACHABLE(CONFIG_INPUT) */ case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */ -- GitLab From dae29b678bb74aa2995c1d2b0fa6b1b91f5d8986 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 13 Jun 2025 06:00:20 +0000 Subject: [PATCH 122/868] ASoC: soc-ops-test: dynamically allocate struct snd_ctl_elem_value This structure is really too larget to be allocated on the stack: linux/sound/soc/soc-ops-test.c:520:1: error: the frame size of\ 1304 bytes is larger than 1280 bytes [-Werror=frame-larger-than=] Change the function to dynamically allocate it instead. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87sek489l8.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-ops-test.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/sound/soc/soc-ops-test.c b/sound/soc/soc-ops-test.c index dc1e482bba6a9..1bafa5626d48b 100644 --- a/sound/soc/soc-ops-test.c +++ b/sound/soc/soc-ops-test.c @@ -481,22 +481,27 @@ static void soc_ops_test_access(struct kunit *test) .private_data = &priv->component, .private_value = (unsigned long)¶m->mc, }; - struct snd_ctl_elem_value result; unsigned int val; int ret; + /* it is too large struct. use kzalloc() */ + struct snd_ctl_elem_value *result; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) + return; ret = regmap_write(priv->component.regmap, 0x0, param->init); KUNIT_ASSERT_FALSE(test, ret); ret = regmap_write(priv->component.regmap, 0x1, param->init); KUNIT_ASSERT_FALSE(test, ret); - result.value.integer.value[0] = param->lctl; - result.value.integer.value[1] = param->rctl; + result->value.integer.value[0] = param->lctl; + result->value.integer.value[1] = param->rctl; - ret = param->put(&kctl, &result); + ret = param->put(&kctl, result); KUNIT_ASSERT_EQ(test, ret, param->ret); if (ret < 0) - return; + goto end; ret = regmap_read(priv->component.regmap, 0x0, &val); KUNIT_ASSERT_FALSE(test, ret); @@ -506,17 +511,19 @@ static void soc_ops_test_access(struct kunit *test) KUNIT_ASSERT_FALSE(test, ret); KUNIT_EXPECT_EQ(test, val, (param->init & ~param->rmask) | param->rreg); - result.value.integer.value[0] = 0; - result.value.integer.value[1] = 0; + result->value.integer.value[0] = 0; + result->value.integer.value[1] = 0; - ret = param->get(&kctl, &result); + ret = param->get(&kctl, result); KUNIT_ASSERT_GE(test, ret, 0); - KUNIT_EXPECT_EQ(test, result.value.integer.value[0], param->lctl); + KUNIT_EXPECT_EQ(test, result->value.integer.value[0], param->lctl); if (param->layout != SOC_OPS_TEST_SINGLE) - KUNIT_EXPECT_EQ(test, result.value.integer.value[1], param->rctl); + KUNIT_EXPECT_EQ(test, result->value.integer.value[1], param->rctl); else - KUNIT_EXPECT_EQ(test, result.value.integer.value[1], 0); + KUNIT_EXPECT_EQ(test, result->value.integer.value[1], 0); +end: + kfree(result); } KUNIT_ARRAY_PARAM(all_info_tests, all_info_test_params, info_test_desc); -- GitLab From 5eb8a0d7733d4cd32a776acf1d1aa1c7c01c8a14 Mon Sep 17 00:00:00 2001 From: Terry Cheong Date: Fri, 13 Jun 2025 16:51:27 +0800 Subject: [PATCH 123/868] ASoC: hdmi-codec: use SND_JACK_AVOUT as jack status Use SND_JACK_AVOUT as the mask to align with hdac_hdmi driver so that we can determine HDMI/DP devices from event type. Most drivers that uses hdmi-codec driver will not be affected since they are creating jacks with SND_JACK_LINEOUT mask. They will still report SND_JACK_LINEOUT when the jack status is updated with snd_soc_jack_report. Signed-off-by: Terry Cheong Link: https://patch.msgid.link/20250613-hdmi-v1-1-665ba7ecd5e7@chromium.org Signed-off-by: Mark Brown --- sound/soc/codecs/hdmi-codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 31121f9c18c91..e1933f733af10 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -943,7 +943,7 @@ static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, { if (jack_status != hcp->jack_status) { if (hcp->jack) - snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); + snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_AVOUT); hcp->jack_status = jack_status; } } @@ -964,7 +964,7 @@ static void plugged_cb(struct device *dev, bool plugged) else snd_show_eld(dev, &hcp->eld_parsed); } - hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); + hdmi_codec_jack_report(hcp, SND_JACK_AVOUT); } else { hdmi_codec_jack_report(hcp, 0); memset(hcp->eld, 0, sizeof(hcp->eld)); @@ -984,7 +984,7 @@ static int hdmi_codec_set_jack(struct snd_soc_component *component, * Report the initial jack status which may have been provided * by the parent hdmi driver while the hpd hook was registered. */ - snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT); + snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_AVOUT); return 0; } -- GitLab From 2acd83beb4d3fdff1e74f56d2f16f7e70f1faa6d Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 14 Jun 2025 08:43:14 +0200 Subject: [PATCH 124/868] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR In an effort of optimising SNDRV_PCM_IOCTL_SYNC_PTR ioctl which is a hot path, lets first refactor the copy from and to user with macros. This is done with macros and not static inline fonctions because types differs between the different versions of snd_pcm_sync_ptr() like functions. First step is to refactor only snd_pcm_ioctl_sync_ptr_compat() and snd_pcm_ioctl_sync_ptr_x32() as it would be a performance regression for snd_pcm_sync_ptr() and snd_pcm_ioctl_sync_ptr_buggy() for now. They may be refactored after next patch. Signed-off-by: Christophe Leroy Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/f8b77932bb9ce96148ae5c3953e7ee44fa2359f8.1749883041.git.christophe.leroy@csgroup.eu --- sound/core/pcm_compat.c | 14 ++------------ sound/core/pcm_native.c | 42 +++++++++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index a42ec7f5a1daf..17540020ac2fe 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -418,9 +418,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, if (snd_BUG_ON(!runtime)) return -EINVAL; - if (get_user(sflags, &src->flags) || - get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - get_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src)) return -EFAULT; if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); @@ -450,15 +448,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (put_user(sstatus.state, &src->s.status.state) || - put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) || - put_user(sstatus.suspended_state, &src->s.status.suspended_state) || - put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) || - put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) || - put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - put_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src)) return -EFAULT; return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ecb71bf1859d4..1f8f6d95b18c7 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3052,6 +3052,34 @@ static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream) return snd_pcm_delay(substream, NULL); } +#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \ + int __err = 0; \ + typeof(*(__ptr)) __user *__src = (__ptr); \ + \ + if (get_user(__f, &src->flags) || \ + get_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \ + get_user(__c.avail_min, &__src->c.control.avail_min)) \ + __err = -EFAULT; \ + __err; \ +}) + +#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \ + int __err = 0; \ + typeof(*(__ptr)) __user *__src = (__ptr); \ + \ + if (put_user(__s.state, &__src->s.status.state) || \ + put_user(__s.hw_ptr, &__src->s.status.hw_ptr) || \ + put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec) || \ + put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec) || \ + put_user(__s.suspended_state, &__src->s.status.suspended_state) || \ + put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec) || \ + put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec) || \ + put_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \ + put_user(__c.avail_min, &__src->c.control.avail_min)) \ + __err = -EFAULT; \ + __err; \ +}) + static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr __user *_sync_ptr) { @@ -3165,9 +3193,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, if (snd_BUG_ON(!runtime)) return -EINVAL; - if (get_user(sflags, &src->flags) || - get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - get_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src)) return -EFAULT; if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); @@ -3200,15 +3226,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, } if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (put_user(sstatus.state, &src->s.status.state) || - put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || - put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || - put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) || - put_user(sstatus.suspended_state, &src->s.status.suspended_state) || - put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) || - put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) || - put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || - put_user(scontrol.avail_min, &src->c.control.avail_min)) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src)) return -EFAULT; return 0; -- GitLab From a9b49bf8ad5905ebd999da345dcff3b9fad43877 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 14 Jun 2025 08:43:15 +0200 Subject: [PATCH 125/868] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() With user access protection (Called SMAP on x86 or KUAP on powerpc) each and every call to get_user() or put_user() performs heavy operations to unlock and lock kernel access to userspace. SNDRV_PCM_IOCTL_SYNC_PTR is a hot path which is called really often and needs to run as fast as possible. To improve performance, perform user accesses by blocks using user_access_begin/user_access_end() and unsafe_get_user()/ unsafe_put_user(). Before the patch the 9 calls to put_user() at the end of snd_pcm_ioctl_sync_ptr_compat() imply the following set of instructions about 9 times (access_ok - enable user - write - disable user): 0.00 : c057f858: 3d 20 7f ff lis r9,32767 0.29 : c057f85c: 39 5e 00 14 addi r10,r30,20 0.77 : c057f860: 61 29 ff fc ori r9,r9,65532 0.32 : c057f864: 7c 0a 48 40 cmplw r10,r9 0.36 : c057f868: 41 a1 fb 58 bgt c057f3c0 0.30 : c057f86c: 3d 20 dc 00 lis r9,-9216 1.95 : c057f870: 7d 3a c3 a6 mtspr 794,r9 0.33 : c057f874: 92 8a 00 00 stw r20,0(r10) 0.27 : c057f878: 3d 20 de 00 lis r9,-8704 0.28 : c057f87c: 7d 3a c3 a6 mtspr 794,r9 ... A perf profile shows that in total the 9 put_user() represent 36% of the time spent in snd_pcm_ioctl() and about 80 instructions. With this patch everything is done in 13 instructions and represent only 15% of the time spent in snd_pcm_ioctl(): 0.57 : c057f5dc: 3d 20 dc 00 lis r9,-9216 0.98 : c057f5e0: 7d 3a c3 a6 mtspr 794,r9 0.16 : c057f5e4: 92 7f 00 04 stw r19,4(r31) 0.63 : c057f5e8: 93 df 00 0c stw r30,12(r31) 0.16 : c057f5ec: 93 9f 00 10 stw r28,16(r31) 4.95 : c057f5f0: 92 9f 00 14 stw r20,20(r31) 0.19 : c057f5f4: 92 5f 00 18 stw r18,24(r31) 0.49 : c057f5f8: 92 bf 00 1c stw r21,28(r31) 0.27 : c057f5fc: 93 7f 00 20 stw r27,32(r31) 5.88 : c057f600: 93 36 00 00 stw r25,0(r22) 0.11 : c057f604: 93 17 00 00 stw r24,0(r23) 0.00 : c057f608: 3d 20 de 00 lis r9,-8704 0.79 : c057f60c: 7d 3a c3 a6 mtspr 794,r9 Note that here the access_ok() in user_write_access_begin() is skipped because the exact same verification has already been performed at the beginning of the fonction with the call to user_read_access_begin(). Signed-off-by: Christophe Leroy Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/eccd047f2dfeb550129a1d60035e2233c4401d0c.1749883041.git.christophe.leroy@csgroup.eu --- sound/core/pcm_native.c | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 1f8f6d95b18c7..5eb59fdb3cb20 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3053,30 +3053,42 @@ static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream) } #define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \ - int __err = 0; \ + __label__ failed, failed_begin; \ + int __err = -EFAULT; \ typeof(*(__ptr)) __user *__src = (__ptr); \ \ - if (get_user(__f, &src->flags) || \ - get_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \ - get_user(__c.avail_min, &__src->c.control.avail_min)) \ - __err = -EFAULT; \ + if (!user_read_access_begin(__src, sizeof(*__src))) \ + goto failed_begin; \ + unsafe_get_user(__f, &__src->flags, failed); \ + unsafe_get_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \ + unsafe_get_user(__c.avail_min, &__src->c.control.avail_min, failed); \ + __err = 0; \ +failed: \ + user_read_access_end(); \ +failed_begin: \ __err; \ }) #define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \ - int __err = 0; \ + __label__ failed, failed_begin; \ + int __err = -EFAULT; \ typeof(*(__ptr)) __user *__src = (__ptr); \ \ - if (put_user(__s.state, &__src->s.status.state) || \ - put_user(__s.hw_ptr, &__src->s.status.hw_ptr) || \ - put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec) || \ - put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec) || \ - put_user(__s.suspended_state, &__src->s.status.suspended_state) || \ - put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec) || \ - put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec) || \ - put_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \ - put_user(__c.avail_min, &__src->c.control.avail_min)) \ - __err = -EFAULT; \ + if (!user_write_access_begin(__src, sizeof(*__src))) \ + goto failed_begin; \ + unsafe_put_user(__s.state, &__src->s.status.state, failed); \ + unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \ + unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec, failed);\ + unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec, failed); \ + unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec, failed); \ + unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \ + unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \ + __err = 0; \ +failed: \ + user_write_access_end(); \ +failed_begin: \ __err; \ }) -- GitLab From c72fad73ba497c237fcc0d771d87a2f8447599bb Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 14 Jun 2025 08:43:16 +0200 Subject: [PATCH 126/868] ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in struct snd_pcm_mmap_status32 To match struct __snd_pcm_mmap_status and enable reuse of snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user() by snd_pcm_sync_ptr() replace tstamp_sec and tstamp_nsec fields by a struct __snd_timespec in struct snd_pcm_mmap_status32. Do the same with audio_tstamp_sec and audio_tstamp_nsec. This is possible because struct snd_pcm_mmap_status32 is packed and __SND_STRUCT_TIME64 is always defined for kernel which means struct __snd_timespec is always defined as: struct __snd_timespec { __s32 tv_sec; __s32 tv_nsec; }; Signed-off-by: Christophe Leroy Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/df8ea1a9aff61c3e358759b1f495bdb9fb8a3e6a.1749883041.git.christophe.leroy@csgroup.eu --- sound/core/pcm_compat.c | 6 ++---- sound/core/pcm_native.c | 14 ++++++-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 17540020ac2fe..54eb9bd8eb218 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -377,12 +377,10 @@ struct snd_pcm_mmap_status_x32 { s32 pad1; u32 hw_ptr; u32 pad2; /* alignment */ - s64 tstamp_sec; - s64 tstamp_nsec; + struct __snd_timespec64 tstamp; snd_pcm_state_t suspended_state; s32 pad3; - s64 audio_tstamp_sec; - s64 audio_tstamp_nsec; + struct __snd_timespec64 audio_tstamp; } __packed; struct snd_pcm_mmap_control_x32 { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 5eb59fdb3cb20..b7339c9ebb1f3 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3078,11 +3078,11 @@ failed_begin: \ goto failed_begin; \ unsafe_put_user(__s.state, &__src->s.status.state, failed); \ unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \ - unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec, failed);\ - unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec, failed); \ + unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp.tv_sec, failed); \ + unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp.tv_nsec, failed); \ unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \ - unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec, failed); \ - unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp.tv_sec, failed); \ + unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp.tv_nsec, failed);\ unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \ unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \ __err = 0; \ @@ -3143,11 +3143,9 @@ struct snd_pcm_mmap_status32 { snd_pcm_state_t state; s32 pad1; u32 hw_ptr; - s32 tstamp_sec; - s32 tstamp_nsec; + struct __snd_timespec tstamp; snd_pcm_state_t suspended_state; - s32 audio_tstamp_sec; - s32 audio_tstamp_nsec; + struct __snd_timespec audio_tstamp; } __packed; struct snd_pcm_mmap_control32 { -- GitLab From 8629eea901b8a672dd096ca783ece8322701ca5a Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Sat, 14 Jun 2025 08:43:17 +0200 Subject: [PATCH 127/868] ALSA: pcm: Convert snd_pcm_sync_ptr() to user_access_begin/user_access_end() Now that snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user() are converted to user_access_begin/user_access_end(), snd_pcm_sync_ptr_get_user() is more efficient than a raw get_user() followed by a copy_from_user(). And because copy_{to/from}_user() are generic functions focussed on transfer of big data blocks to/from user, snd_pcm_sync_ptr_put_user() is also more efficient for small amont of data. So use snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user() in snd_pcm_sync_ptr() too. snd_pcm_ioctl_sync_ptr_buggy() is left as it is because the conversion wouldn't be straigh-forward due to the workaround it provides. Signed-off-by: Christophe Leroy Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/6ce6bc4da498ea7ea2be5f279b374370b1613b13.1749883041.git.christophe.leroy@csgroup.eu --- sound/core/pcm_native.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index b7339c9ebb1f3..1eab940fa2e5a 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3096,45 +3096,43 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr __user *_sync_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_sync_ptr sync_ptr; volatile struct snd_pcm_mmap_status *status; volatile struct snd_pcm_mmap_control *control; + u32 sflags; + struct snd_pcm_mmap_control scontrol; + struct snd_pcm_mmap_status sstatus; int err; - memset(&sync_ptr, 0, sizeof(sync_ptr)); - if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags))) + if (snd_pcm_sync_ptr_get_user(sflags, scontrol, _sync_ptr)) return -EFAULT; - if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control))) - return -EFAULT; status = runtime->status; control = runtime->control; - if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) { + if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { err = snd_pcm_hwsync(substream); if (err < 0) return err; } scoped_guard(pcm_stream_lock_irq, substream) { - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { - err = pcm_lib_apply_appl_ptr(substream, - sync_ptr.c.control.appl_ptr); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr); if (err < 0) return err; } else { - sync_ptr.c.control.appl_ptr = control->appl_ptr; + scontrol.appl_ptr = control->appl_ptr; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) - control->avail_min = sync_ptr.c.control.avail_min; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; else - sync_ptr.c.control.avail_min = control->avail_min; - sync_ptr.s.status.state = status->state; - sync_ptr.s.status.hw_ptr = status->hw_ptr; - sync_ptr.s.status.tstamp = status->tstamp; - sync_ptr.s.status.suspended_state = status->suspended_state; - sync_ptr.s.status.audio_tstamp = status->audio_tstamp; + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; } - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); - if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) + if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, _sync_ptr)) return -EFAULT; return 0; } -- GitLab From b2904df0a347f206900681f566bb53fc42bd8ba7 Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Mon, 16 Jun 2025 11:56:07 +0800 Subject: [PATCH 128/868] ALSA: hda/tas2781: Add compatible for hardware id TIAS2781 and TXNW2781 TIAS2781 is unofficial hardware id in ACPI for tas2781 in HDA, has been used for several projects. TXNW is the official hardware id for TI, will be used in new projects, including device on SPI bus, which was enumerated by drivers/acpi/scan.c, and probed by smi_probe() in drivers/platform/x86/serial-multi-instantiate.c. This patch will support both TIAS2781 and TXNW2781 in ACPI with tas2781 under HDA. As our I2C driver will handle all of slaver devices, so we probe first device only: "TXNW2781:00-tas2781-hda.0" Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20250616035607.2569-1-baojun.xu@ti.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 21 +++++++++++++++++---- sound/pci/hda/tas2781_hda_i2c.c | 6 ++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7db62477b7857..84a68c7d65b74 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7328,7 +7328,7 @@ static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fix alc_fixup_inv_dmic(cdc, fix, action); } -static void tas2781_fixup_i2c(struct hda_codec *cdc, +static void tas2781_fixup_tias_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); @@ -7339,6 +7339,12 @@ static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); } +static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1); +} + static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { @@ -8007,6 +8013,7 @@ enum { ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, ALC245_FIXUP_TAS2781_SPI_2, + ALC287_FIXUP_TXNW2781_I2C, ALC287_FIXUP_YOGA7_14ARB7_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, @@ -10261,7 +10268,7 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC287_FIXUP_TAS2781_I2C] = { .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_i2c, + .v.func = tas2781_fixup_tias_i2c, .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, @@ -10271,6 +10278,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, + [ALC287_FIXUP_TXNW2781_I2C] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_txnw_i2c, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, + }, [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { .type = HDA_FIXUP_FUNC, .v.func = yoga7_14arb7_fixup_i2c, @@ -11329,8 +11342,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index eb2295b490d94..b9cdbca951e4e 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -587,6 +587,11 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) device_name = "TIAS2781"; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; + } else if (strstarts(dev_name(&clt->dev), + "TXNW2781:00-tas2781-hda.0")) { + device_name = "TXNW2781"; + hda_priv->save_calibration = tas2781_save_calibration; + tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; } else if (strstr(dev_name(&clt->dev), "INT8866")) { device_name = "INT8866"; hda_priv->save_calibration = tas2563_save_calibration; @@ -725,6 +730,7 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = { static const struct acpi_device_id tas2781_acpi_hda_match[] = { {"INT8866", 0 }, {"TIAS2781", 0 }, + {"TXNW2781", 0 }, {} }; MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); -- GitLab From 0a99f2d8ff5b31c3aaa70b23bde58692d1300bdc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 11 Jun 2025 13:50:26 -0500 Subject: [PATCH 129/868] gpio: virtuser: use gpiod_multi_set_value_cansleep() Reduce verbosity by using gpiod_multi_set_value_cansleep() instead of gpiod_set_array_value_cansleep(). Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20250611-gpio-virtuser-use-gpiod_multi_set_value_cansleep-v1-1-43b4adf6c807@baylibre.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-virtuser.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index eab6726953b41..a10eab7d2617e 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -215,9 +215,7 @@ static int gpio_virtuser_set_array_value(struct gpio_descs *descs, struct gpio_virtuser_irq_work_context ctx; if (!atomic) - return gpiod_set_array_value_cansleep(descs->ndescs, - descs->desc, - descs->info, values); + return gpiod_multi_set_value_cansleep(descs, values); gpio_virtuser_init_irq_work_context(&ctx); ctx.work = IRQ_WORK_INIT_HARD(gpio_virtuser_set_value_array_atomic); -- GitLab From 7b2c2f1eb3914f5214a5b2ae966d7d7bb0057582 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 11 Jun 2025 12:43:34 +0200 Subject: [PATCH 130/868] gpio: Use dev_fwnode() where applicable across drivers irq_domain_create_simple() takes fwnode as the first argument. It can be extracted from the struct device using dev_fwnode() helper instead of using of_node with of_fwnode_handle(). So use the dev_fwnode() helper where applicable. Signed-off-by: Jiri Slaby (SUSE) Link: https://lore.kernel.org/r/20250611104348.192092-6-jirislaby@kernel.org [Bartosz: tweaked the commit message] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-brcmstb.c | 6 ++---- drivers/gpio/gpio-davinci.c | 2 +- drivers/gpio/gpio-em.c | 3 +-- drivers/gpio/gpio-grgpio.c | 5 ++--- drivers/gpio/gpio-lpc18xx.c | 4 ++-- drivers/gpio/gpio-mvebu.c | 4 ++-- drivers/gpio/gpio-mxc.c | 4 ++-- drivers/gpio/gpio-mxs.c | 2 +- drivers/gpio/gpio-pxa.c | 5 ++--- drivers/gpio/gpio-rockchip.c | 4 ++-- drivers/gpio/gpio-sodaville.c | 4 ++-- drivers/gpio/gpio-tb10x.c | 5 ++--- drivers/gpio/gpio-twl4030.c | 2 +- 13 files changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index e7671bcd5c07a..e29a9589b3ccb 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -436,10 +436,8 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, struct device_node *np = dev->of_node; int err; - priv->irq_domain = - irq_domain_create_linear(of_fwnode_handle(np), priv->num_gpios, - &brcmstb_gpio_irq_domain_ops, - priv); + priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev), priv->num_gpios, + &brcmstb_gpio_irq_domain_ops, priv); if (!priv->irq_domain) { dev_err(dev, "Couldn't allocate IRQ domain\n"); return -ENXIO; diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 80a82492171e4..8f3a36d0191d3 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -478,7 +478,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) return irq; } - irq_domain = irq_domain_create_legacy(of_fwnode_handle(dev->of_node), ngpio, irq, 0, + irq_domain = irq_domain_create_legacy(dev_fwnode(dev), ngpio, irq, 0, &davinci_gpio_irq_ops, chips); if (!irq_domain) { dev_err(dev, "Couldn't register an IRQ domain\n"); diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index a5e6e446f39ce..015f1ac32dd9e 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -325,8 +325,7 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_release_resources = em_gio_irq_relres; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_create_simple(of_fwnode_handle(dev->of_node), - ngpios, 0, + p->irq_domain = irq_domain_create_simple(dev_fwnode(dev), ngpios, 0, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { dev_err(dev, "cannot initialize irq domain\n"); diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index d38a2d9854ca5..f3f8bab62f94c 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -402,9 +402,8 @@ static int grgpio_probe(struct platform_device *ofdev) return -EINVAL; } - priv->domain = irq_domain_create_linear(of_fwnode_handle(np), gc->ngpio, - &grgpio_irq_domain_ops, - priv); + priv->domain = irq_domain_create_linear(dev_fwnode(&ofdev->dev), gc->ngpio, + &grgpio_irq_domain_ops, priv); if (!priv->domain) { dev_err(dev, "Could not add irq domain\n"); return -EINVAL; diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index b0a8da5c058d4..2dbfbf90176ce 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -249,8 +249,8 @@ static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc) raw_spin_lock_init(&ic->lock); ic->domain = irq_domain_create_hierarchy(parent_domain, 0, NR_LPC18XX_GPIO_PIN_IC_IRQS, - of_fwnode_handle(dev->of_node), - &lpc18xx_gpio_pin_ic_domain_ops, ic); + dev_fwnode(dev), &lpc18xx_gpio_pin_ic_domain_ops, + ic); if (!ic->domain) { pr_err("unable to add irq domain\n"); ret = -ENODEV; diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 57633a7b42700..24792b8eb0839 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1236,8 +1236,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev) if (!have_irqs) return 0; - mvchip->domain = - irq_domain_create_linear(of_fwnode_handle(np), ngpios, &irq_generic_chip_ops, NULL); + mvchip->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), ngpios, + &irq_generic_chip_ops, NULL); if (!mvchip->domain) { dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n", mvchip->chip.label); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index fae1a30f8ae6a..4af5a2972d12f 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -509,8 +509,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) goto out_bgio; } - port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, - &irq_domain_simple_ops, NULL); + port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, + &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; goto out_bgio; diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index b418fbccb26c0..0ea46f3d04e12 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -303,7 +303,7 @@ static int mxs_gpio_probe(struct platform_device *pdev) goto out_iounmap; } - port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, + port->domain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), 32, irq_base, 0, &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index aead35ea090e6..131ab79ebce75 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -642,9 +642,8 @@ static int pxa_gpio_probe(struct platform_device *pdev) if (!pxa_last_gpio) return -EINVAL; - pchip->irqdomain = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), - pxa_last_gpio + 1, irq_base, 0, - &pxa_irq_domain_ops, pchip); + pchip->irqdomain = irq_domain_create_legacy(dev_fwnode(&pdev->dev), pxa_last_gpio + 1, + irq_base, 0, &pxa_irq_domain_ops, pchip); if (!pchip->irqdomain) return -ENOMEM; diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index c63352f2f1ec7..64700a003f9a1 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -521,8 +521,8 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank) struct irq_chip_generic *gc; int ret; - bank->domain = irq_domain_create_linear(of_fwnode_handle(bank->of_node), 32, - &irq_generic_chip_ops, NULL); + bank->domain = irq_domain_create_linear(dev_fwnode(bank->dev), 32, &irq_generic_chip_ops, + NULL); if (!bank->domain) { dev_warn(bank->dev, "could not init irq domain for bank %s\n", bank->name); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index 6a3c4c6251388..abd13c79ace09 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -169,8 +169,8 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); - sd->id = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), SDV_NUM_PUB_GPIOS, - sd->irq_base, 0, &irq_domain_sdv_ops, sd); + sd->id = irq_domain_create_legacy(dev_fwnode(&pdev->dev), SDV_NUM_PUB_GPIOS, sd->irq_base, + 0, &irq_domain_sdv_ops, sd); if (!sd->id) return -ENODEV; diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 8cf676fd0a0bb..1869ee7f9423e 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -183,9 +183,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev) if (ret != 0) return ret; - tb10x_gpio->domain = irq_domain_create_linear(of_fwnode_handle(np), - tb10x_gpio->gc.ngpio, - &irq_generic_chip_ops, NULL); + tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev), tb10x_gpio->gc.ngpio, + &irq_generic_chip_ops, NULL); if (!tb10x_gpio->domain) { return -ENOMEM; } diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 0d17985a5fdc4..c5d7825f19c18 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -523,7 +523,7 @@ static int gpio_twl4030_probe(struct platform_device *pdev) return irq_base; } - irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), TWL4030_GPIO_MAX, irq_base, 0, + irq_domain_create_legacy(dev_fwnode(&pdev->dev), TWL4030_GPIO_MAX, irq_base, 0, &irq_domain_simple_ops, NULL); ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base); -- GitLab From bddfad9f7ef3fc73f0a6fb05996719adcb5082fc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 13 Jun 2025 09:16:28 +0200 Subject: [PATCH 131/868] gpio: sloppy-logic-analyzer: Fully open-code compatible for grepping It is very useful to find driver implementing compatibles with `git grep compatible`, so driver should not use defines for that string, even if this means string will be effectively duplicated. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20250613071627.46687-2-krzysztof.kozlowski@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sloppy-logic-analyzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 8cf3b171c599b..969dddd3d6faf 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -306,7 +306,7 @@ static void gpio_la_poll_remove(struct platform_device *pdev) } static const struct of_device_id gpio_la_poll_of_match[] = { - { .compatible = GPIO_LA_NAME }, + { .compatible = "gpio-sloppy-logic-analyzer" }, { } }; MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match); -- GitLab From 367864935785382bab95f5e5a691535d28f5a21b Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 14 Jun 2025 13:12:16 +0200 Subject: [PATCH 132/868] gpio: raspberrypi-exp: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Stefan Wahren Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20250614111216.93677-1-wahrenst@gmx.net Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-raspberrypi-exp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index 9d1b95e429f19..b4b607515a044 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -175,7 +175,7 @@ static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off) return !!get.state; } -static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +static int rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) { struct rpi_exp_gpio *gpio; struct gpio_get_set_state set; @@ -188,10 +188,14 @@ static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE, &set, sizeof(set)); - if (ret || set.gpio != 0) + if (ret || set.gpio != 0) { dev_err(gc->parent, "Failed to set GPIO %u state (%d %x)\n", off, ret, set.gpio); + return ret ? ret : -EIO; + } + + return 0; } static int rpi_exp_gpio_probe(struct platform_device *pdev) @@ -228,7 +232,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev) rpi_gpio->gc.direction_output = rpi_exp_gpio_dir_out; rpi_gpio->gc.get_direction = rpi_exp_gpio_get_direction; rpi_gpio->gc.get = rpi_exp_gpio_get; - rpi_gpio->gc.set = rpi_exp_gpio_set; + rpi_gpio->gc.set_rv = rpi_exp_gpio_set; rpi_gpio->gc.can_sleep = true; return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio); -- GitLab From 7954001a769a174f70d770c360fbff0d99dc51d4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 13 Jun 2025 14:49:22 -0700 Subject: [PATCH 133/868] thermal: intel: int340x: Add throttling control interface to PTC Firmware-based thermal temperature control loops may aggressively throttle performance to prevent temperature overshoots relative to the defined target temperature. This can negatively impact performance. User space may prefer to prioritize performance, even if it results in temperature overshoots with in acceptable range. For example, user space might tolerate temperature overshoots when the device is placed on a desk, as opposed to when it's on a lap. To accommodate such scenarios, an optional attribute is provided to specify a tolerance level for temperature overshoots while maintaining acceptable performance. Attribute: thermal_tolerance: This attribute ranges from 0 to 7, where 0 represents the most aggressive control to avoid any temperature overshoots, and 7 represents a more graceful approach, favoring performance even at the expense of temperature overshoots. Note: This level may not scale linearly. For example, a value of 3 does not necessarily imply a 50% improvement in performance compared to a value of 0. Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Link: https://patch.msgid.link/20250613214923.2910397-1-srinivas.pandruvada@linux.intel.com Signed-off-by: Rafael J. Wysocki --- Documentation/driver-api/thermal/intel_dptf.rst | 9 +++++++++ .../intel/int340x_thermal/platform_temperature_control.c | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/thermal/intel_dptf.rst b/Documentation/driver-api/thermal/intel_dptf.rst index ec5769accae0f..c51ac793dc06e 100644 --- a/Documentation/driver-api/thermal/intel_dptf.rst +++ b/Documentation/driver-api/thermal/intel_dptf.rst @@ -206,6 +206,15 @@ All these controls needs admin privilege to update. Update a new temperature target in milli degree celsius for hardware to use for the temperature control. +``thermal_tolerance`` (RW) + This attribute ranges from 0 to 7, where 0 represents + the most aggressive control to avoid any temperature overshoots, and + 7 represents a more graceful approach, favoring performance even at + the expense of temperature overshoots. + Note: This level may not scale linearly. For example, a value of 3 does + not necessarily imply a 50% improvement in performance compared to a + value of 0. + Given that this is platform temperature control, it is expected that a single user-level manager owns and manages the controls. If multiple user-level software applications attempt to write different targets, it diff --git a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c index 2d65045148936..7850e91a6e2cd 100644 --- a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -49,7 +49,7 @@ struct mmio_reg { }; #define MAX_ATTR_GROUP_NAME_LEN 32 -#define PTC_MAX_ATTRS 3 +#define PTC_MAX_ATTRS 4 struct ptc_data { u32 offset; @@ -57,6 +57,7 @@ struct ptc_data { struct attribute *ptc_attrs[PTC_MAX_ATTRS]; struct device_attribute temperature_target_attr; struct device_attribute enable_attr; + struct device_attribute thermal_tolerance_attr; char group_name[MAX_ATTR_GROUP_NAME_LEN]; }; @@ -78,6 +79,7 @@ static u32 ptc_offsets[PTC_MAX_INSTANCES] = {0x5B20, 0x5B28, 0x5B30}; static const char * const ptc_strings[] = { "temperature_target", "enable", + "thermal_tolerance", NULL }; @@ -177,6 +179,8 @@ PTC_SHOW(temperature_target); PTC_STORE(temperature_target); PTC_SHOW(enable); PTC_STORE(enable); +PTC_SHOW(thermal_tolerance); +PTC_STORE(thermal_tolerance); #define ptc_init_attribute(_name)\ do {\ @@ -193,9 +197,11 @@ static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data ptc_init_attribute(temperature_target); ptc_init_attribute(enable); + ptc_init_attribute(thermal_tolerance); data->ptc_attrs[index++] = &data->temperature_target_attr.attr; data->ptc_attrs[index++] = &data->enable_attr.attr; + data->ptc_attrs[index++] = &data->thermal_tolerance_attr.attr; data->ptc_attrs[index] = NULL; snprintf(data->group_name, MAX_ATTR_GROUP_NAME_LEN, -- GitLab From ea78eed7a451375fc8d604bbe3db55be5779eb7d Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 13 Jun 2025 14:49:23 -0700 Subject: [PATCH 134/868] thermal: intel: int340x: Allow temperature override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add debugfs interface to override hardware provide temperature. This interface can be used primarily for debug. Alternatively this can be also used to use hardware control loops to manage temperature for virtual sensors. Virtual sensors are soft sensors created by kernel/ user space aggregating other sensors. There are three attributes to override the maximum three instances of platform temperature control. /sys/kernel/debug/platform_temperature_control/ ├── temperature_0 ├── temperature_1 └── temperature_2 These are write only attributes requires admin privilege. Any value greater than 0, will override the temperature. A value of 0 will stop overriding the temperature. Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Link: https://patch.msgid.link/20250613214923.2910397-2-srinivas.pandruvada@linux.intel.com Signed-off-by: Rafael J. Wysocki --- .../platform_temperature_control.c | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c index 7850e91a6e2cd..0ccc72c93499a 100644 --- a/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c +++ b/drivers/thermal/intel/int340x_thermal/platform_temperature_control.c @@ -38,6 +38,7 @@ #include #include +#include #include #include "processor_thermal_device.h" @@ -53,6 +54,7 @@ struct mmio_reg { struct ptc_data { u32 offset; + struct pci_dev *pdev; struct attribute_group ptc_attr_group; struct attribute *ptc_attrs[PTC_MAX_ATTRS]; struct device_attribute temperature_target_attr; @@ -215,6 +217,63 @@ static int ptc_create_groups(struct pci_dev *pdev, int instance, struct ptc_data } static struct ptc_data ptc_instance[PTC_MAX_INSTANCES]; +static struct dentry *ptc_debugfs; + +#define PTC_TEMP_OVERRIDE_ENABLE_INDEX 4 +#define PTC_TEMP_OVERRIDE_INDEX 5 + +static ssize_t ptc_temperature_write(struct file *file, const char __user *data, + size_t count, loff_t *ppos) +{ + struct ptc_data *ptc_instance = file->private_data; + struct pci_dev *pdev = ptc_instance->pdev; + char buf[32]; + ssize_t len; + u32 value; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, data, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtouint(buf, 0, &value)) + return -EINVAL; + + if (ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units) + value /= ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].units; + + if (value > ptc_mmio_regs[PTC_TEMP_OVERRIDE_INDEX].mask) + return -EINVAL; + + if (!value) { + ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 0); + } else { + ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_INDEX, value); + ptc_mmio_write(pdev, ptc_instance->offset, PTC_TEMP_OVERRIDE_ENABLE_INDEX, 1); + } + + return count; +} + +static const struct file_operations ptc_fops = { + .open = simple_open, + .write = ptc_temperature_write, + .llseek = generic_file_llseek, +}; + +static void ptc_create_debugfs(void) +{ + ptc_debugfs = debugfs_create_dir("platform_temperature_control", NULL); + + debugfs_create_file("temperature_0", 0200, ptc_debugfs, &ptc_instance[0], &ptc_fops); + debugfs_create_file("temperature_1", 0200, ptc_debugfs, &ptc_instance[1], &ptc_fops); + debugfs_create_file("temperature_2", 0200, ptc_debugfs, &ptc_instance[2], &ptc_fops); +} + +static void ptc_delete_debugfs(void) +{ + debugfs_remove_recursive(ptc_debugfs); +} int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) { @@ -223,8 +282,11 @@ int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_ for (i = 0; i < PTC_MAX_INSTANCES; i++) { ptc_instance[i].offset = ptc_offsets[i]; + ptc_instance[i].pdev = pdev; ptc_create_groups(pdev, i, &ptc_instance[i]); } + + ptc_create_debugfs(); } return 0; @@ -240,6 +302,8 @@ void proc_thermal_ptc_remove(struct pci_dev *pdev) for (i = 0; i < PTC_MAX_INSTANCES; i++) sysfs_remove_group(&pdev->dev.kobj, &ptc_instance[i].ptc_attr_group); + + ptc_delete_debugfs(); } } EXPORT_SYMBOL_GPL(proc_thermal_ptc_remove); -- GitLab From e7af416aebb36e6681b9c6950d0f6352aee7c084 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 16 Jun 2025 11:30:52 +0100 Subject: [PATCH 135/868] firmware: cs_dsp: Remove unused struct list_head from cs_dsp_coeff_ctl Remove two unused pointers from struct cs_dsp_coeff_ctl by taking the struct list_head out of struct cs_dsp_alg_region. On a x86_64 build this saves 16 bytes per control. Each cs_dsp_coeff_ctl instance needs to keep information about the algorithm region it refers to. This is done by embedding an instance of struct cs_dsp_alg_region. But cs_dsp_alg_region was also used to store entries in a list of algorithm regions, and so had a struct list_head object for that purpose. This list_head object is not used with the embedded object in struct cs_dsp_alg_region so was just wasted bytes. A new struct cs_dsp_alg_region_list_item has been defined for creating the list of algorithm regions. It contains a struct cs_dsp_alg_region and a struct list_head. Signed-off-by: Richard Fitzgerald Link: https://patch.msgid.link/20250616103052.66537-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/firmware/cirrus/cs_dsp.c | 45 ++++++++++++++------------ include/linux/firmware/cirrus/cs_dsp.h | 2 -- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 560724ce21aa3..f51047d8ea64f 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -311,6 +311,11 @@ static const struct cs_dsp_ops cs_dsp_adsp2_ops[]; static const struct cs_dsp_ops cs_dsp_halo_ops; static const struct cs_dsp_ops cs_dsp_halo_ao_ops; +struct cs_dsp_alg_region_list_item { + struct list_head list; + struct cs_dsp_alg_region alg_region; +}; + struct cs_dsp_buf { struct list_head list; void *buf; @@ -1752,13 +1757,13 @@ static void *cs_dsp_read_algs(struct cs_dsp *dsp, size_t n_algs, struct cs_dsp_alg_region *cs_dsp_find_alg_region(struct cs_dsp *dsp, int type, unsigned int id) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; lockdep_assert_held(&dsp->pwr_lock); - list_for_each_entry(alg_region, &dsp->alg_regions, list) { - if (id == alg_region->alg && type == alg_region->type) - return alg_region; + list_for_each_entry(item, &dsp->alg_regions, list) { + if (id == item->alg_region.alg && type == item->alg_region.type) + return &item->alg_region; } return NULL; @@ -1769,35 +1774,35 @@ static struct cs_dsp_alg_region *cs_dsp_create_region(struct cs_dsp *dsp, int type, __be32 id, __be32 ver, __be32 base) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; - alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); - if (!alg_region) + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) return ERR_PTR(-ENOMEM); - alg_region->type = type; - alg_region->alg = be32_to_cpu(id); - alg_region->ver = be32_to_cpu(ver); - alg_region->base = be32_to_cpu(base); + item->alg_region.type = type; + item->alg_region.alg = be32_to_cpu(id); + item->alg_region.ver = be32_to_cpu(ver); + item->alg_region.base = be32_to_cpu(base); - list_add_tail(&alg_region->list, &dsp->alg_regions); + list_add_tail(&item->list, &dsp->alg_regions); if (dsp->wmfw_ver > 0) - cs_dsp_ctl_fixup_base(dsp, alg_region); + cs_dsp_ctl_fixup_base(dsp, &item->alg_region); - return alg_region; + return &item->alg_region; } static void cs_dsp_free_alg_regions(struct cs_dsp *dsp) { - struct cs_dsp_alg_region *alg_region; + struct cs_dsp_alg_region_list_item *item; while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct cs_dsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); + item = list_first_entry(&dsp->alg_regions, + struct cs_dsp_alg_region_list_item, + list); + list_del(&item->list); + kfree(item); } } diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 7cae703b3137c..a66eb7624730c 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -64,14 +64,12 @@ struct cs_dsp_region { /** * struct cs_dsp_alg_region - Describes a logical algorithm region in DSP address space - * @list: List node for internal use * @alg: Algorithm id * @ver: Expected algorithm version * @type: Memory region type * @base: Address of region */ struct cs_dsp_alg_region { - struct list_head list; unsigned int alg; unsigned int ver; int type; -- GitLab From 47972c1c3315672352f25c68f91dd88543541947 Mon Sep 17 00:00:00 2001 From: Sachin Mokashi Date: Fri, 13 Jun 2025 12:35:30 -0400 Subject: [PATCH 136/868] ASoC: Intel: Replace deprecated strcpy() with strscpy() strcpy() is deprecated, use strscpy() instead. As strcpy() performs no bounds checking on the destination buffer. This could result in buffer overflow. The safe replacement is strscpy(). Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Sachin Mokashi Link: https://patch.msgid.link/20250613163530.1165690-1-sachin.mokashi@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/cht_bsw_rt5672.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index aaef212cf44ec..54c1894ee96ac 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -458,7 +459,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (!drv) return -ENOMEM; - strcpy(drv->codec_name, RT5672_I2C_DEFAULT); + strscpy(drv->codec_name, RT5672_I2C_DEFAULT, sizeof(drv->codec_name)); /* find index of codec dai */ for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) { -- GitLab From dce4bc30f42d313b4dc5832316196411b7f07ad0 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 16 Jun 2025 11:19:55 +0200 Subject: [PATCH 137/868] spi: spi-fsl-dspi: Revert unintended dependency change in config SPI_FSL_DSPI Commit 9a30e332c36c ("spi: spi-fsl-dspi: Enable support for S32G platforms") reworks the dependencies of config SPI_FSL_DSPI, but introduces a typo changing the dependency to M5441x to a dependency on a non-existing config M54541x. Revert the unintended change to depend on the config M5441x. Fixes: 9a30e332c36c ("spi: spi-fsl-dspi: Enable support for S32G platforms") Signed-off-by: Lukas Bulwahn Reviewed-by: James Clark Link: https://patch.msgid.link/20250616091955.20547-1-lukas.bulwahn@redhat.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 60eb65c927b14..f2d2295a5501c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -647,7 +647,7 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" select REGMAP_MMIO - depends on ARCH_MXC || ARCH_NXP || M54541x || COMPILE_TEST + depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST help This enables support for the Freescale DSPI controller in master mode. S32, VF610, LS1021A and ColdFire platforms uses the controller. -- GitLab From d03b53c9139352b744ed007bf562bd35517bacff Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 16 Jun 2025 09:43:18 +0200 Subject: [PATCH 138/868] dt-bindings: gpio: gpio-xilinx: Mark clocks as required property On Microblaze platforms there is no need to handle clocks because the system is starting with clocks enabled (can be described via fixed clock node or clock-frequency property or not described at all). With using soft IPs with SOC platforms there is mandatory to handle clocks as is explained in commit 60dbdc6e08d6 ("dt-bindings: net: emaclite: Add clock support"). That's why make clock as required in dt binding because it is present in both configurations and should be described even there is no way how to handle it on Microblaze systems. There is also need to describe missing axi gpio clock in fpga-region.yaml not to introduce new error when make dt_binding_check runs. Signed-off-by: Michal Simek Reviewed-by: Xu Yilun # fpga Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/94151cfbcff5e4ae05894981c7e398b605d4b00a.1750059796.git.michal.simek@amd.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/fpga/fpga-region.yaml | 1 + Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.yaml b/Documentation/devicetree/bindings/fpga/fpga-region.yaml index 77554885a6c49..7d2d3b7aa4b7e 100644 --- a/Documentation/devicetree/bindings/fpga/fpga-region.yaml +++ b/Documentation/devicetree/bindings/fpga/fpga-region.yaml @@ -316,6 +316,7 @@ examples: reg = <0x40000000 0x10000>; gpio-controller; #gpio-cells = <2>; + clocks = <&clk>; }; }; diff --git a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml index 8fbf12ca067ee..7af4eb2d18588 100644 --- a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml +++ b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml @@ -117,6 +117,7 @@ properties: required: - reg - compatible + - clocks - gpio-controller - "#gpio-cells" -- GitLab From b908d35d0003cc75d4ebf7c24a61b07d34e7f5dc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:11 +0200 Subject: [PATCH 139/868] gpio: mmio: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-1-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 53 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4841e4ebe7a67..9169eccadb238 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -211,11 +211,12 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask, return 0; } -static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) { + return 0; } -static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; @@ -230,10 +231,12 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) gc->write_reg(gc->reg_dat, gc->bgpio_data); raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; } -static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, - int val) +static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) { unsigned long mask = bgpio_line2mask(gc, gpio); @@ -241,9 +244,11 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, gc->write_reg(gc->reg_set, mask); else gc->write_reg(gc->reg_clr, mask); + + return 0; } -static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; @@ -258,6 +263,8 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) gc->write_reg(gc->reg_set, gc->bgpio_data); raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; } static void bgpio_multiple_get_masks(struct gpio_chip *gc, @@ -298,21 +305,25 @@ static void bgpio_set_multiple_single_reg(struct gpio_chip *gc, raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); } -static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, +static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_dat); + + return 0; } -static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_set); + + return 0; } -static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, - unsigned long *mask, - unsigned long *bits) +static int bgpio_set_multiple_with_clear(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits) { unsigned long set_mask, clear_mask; @@ -322,6 +333,8 @@ static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, gc->write_reg(gc->reg_set, set_mask); if (clear_mask) gc->write_reg(gc->reg_clr, clear_mask); + + return 0; } static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out) @@ -510,18 +523,18 @@ static int bgpio_setup_io(struct gpio_chip *gc, if (set && clr) { gc->reg_set = set; gc->reg_clr = clr; - gc->set = bgpio_set_with_clear; - gc->set_multiple = bgpio_set_multiple_with_clear; + gc->set_rv = bgpio_set_with_clear; + gc->set_multiple_rv = bgpio_set_multiple_with_clear; } else if (set && !clr) { gc->reg_set = set; - gc->set = bgpio_set_set; - gc->set_multiple = bgpio_set_multiple_set; + gc->set_rv = bgpio_set_set; + gc->set_multiple_rv = bgpio_set_multiple_set; } else if (flags & BGPIOF_NO_OUTPUT) { - gc->set = bgpio_set_none; - gc->set_multiple = NULL; + gc->set_rv = bgpio_set_none; + gc->set_multiple_rv = NULL; } else { - gc->set = bgpio_set; - gc->set_multiple = bgpio_set_multiple; + gc->set_rv = bgpio_set; + gc->set_multiple_rv = bgpio_set_multiple; } if (!(flags & BGPIOF_UNREADABLE_REG_SET) && @@ -654,7 +667,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, } gc->bgpio_data = gc->read_reg(gc->reg_dat); - if (gc->set == bgpio_set_set && + if (gc->set_rv == bgpio_set_set && !(flags & BGPIOF_UNREADABLE_REG_SET)) gc->bgpio_data = gc->read_reg(gc->reg_set); -- GitLab From d27746181905c256eced857f4b2c051ac44b0b45 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:12 +0200 Subject: [PATCH 140/868] gpio: mm-lantiq: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-2-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mm-lantiq.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c index 14ae257834381..897a1e004681c 100644 --- a/drivers/gpio/gpio-mm-lantiq.c +++ b/drivers/gpio/gpio-mm-lantiq.c @@ -55,9 +55,9 @@ static void ltq_mm_apply(struct ltq_mm *chip) * @gpio: GPIO signal number. * @val: Value to be written to specified signal. * - * Set the shadow value and call ltq_mm_apply. + * Set the shadow value and call ltq_mm_apply. Always returns 0. */ -static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) +static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value) { struct ltq_mm *chip = gpiochip_get_data(gc); @@ -66,6 +66,8 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) else chip->shadow &= ~(1 << offset); ltq_mm_apply(chip); + + return 0; } /** @@ -78,9 +80,7 @@ static void ltq_mm_set(struct gpio_chip *gc, unsigned offset, int value) */ static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value) { - ltq_mm_set(gc, offset, value); - - return 0; + return ltq_mm_set(gc, offset, value); } /** @@ -111,7 +111,7 @@ static int ltq_mm_probe(struct platform_device *pdev) chip->mmchip.gc.ngpio = 16; chip->mmchip.gc.direction_output = ltq_mm_dir_out; - chip->mmchip.gc.set = ltq_mm_set; + chip->mmchip.gc.set_rv = ltq_mm_set; chip->mmchip.save_regs = ltq_mm_save_regs; /* store the shadow value if one was passed by the devicetree */ -- GitLab From b454580cf11b45a9da22821543f1455a6a31c5ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:13 +0200 Subject: [PATCH 141/868] gpio: moxtet: use new GPIO line value setter callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Marek Behún Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-3-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-moxtet.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c index 61f9efd6c64fb..27dd9c3e7b771 100644 --- a/drivers/gpio/gpio-moxtet.c +++ b/drivers/gpio/gpio-moxtet.c @@ -52,15 +52,15 @@ static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset) return !!(ret & BIT(offset)); } -static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, - int val) +static int moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) { struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); int state; state = moxtet_device_written(chip->dev); if (state < 0) - return; + return state; offset -= MOXTET_GPIO_INPUTS; @@ -69,7 +69,7 @@ static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, else state &= ~BIT(offset); - moxtet_device_write(chip->dev, state); + return moxtet_device_write(chip->dev, state); } static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -104,13 +104,11 @@ static int moxtet_gpio_direction_output(struct gpio_chip *gc, struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); if (chip->desc->out_mask & BIT(offset)) - moxtet_gpio_set_value(gc, offset, val); + return moxtet_gpio_set_value(gc, offset, val); else if (chip->desc->in_mask & BIT(offset)) return -ENOTSUPP; - else - return -EINVAL; - return 0; + return -EINVAL; } static int moxtet_gpio_probe(struct device *dev) @@ -142,7 +140,7 @@ static int moxtet_gpio_probe(struct device *dev) chip->gpio_chip.direction_input = moxtet_gpio_direction_input; chip->gpio_chip.direction_output = moxtet_gpio_direction_output; chip->gpio_chip.get = moxtet_gpio_get_value; - chip->gpio_chip.set = moxtet_gpio_set_value; + chip->gpio_chip.set_rv = moxtet_gpio_set_value; chip->gpio_chip.base = -1; chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS; -- GitLab From 80d42372d9d87626b55516779e935c012cecdae7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:14 +0200 Subject: [PATCH 142/868] gpio: mpc5200: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-4-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mpc5200.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mpc5200.c b/drivers/gpio/gpio-mpc5200.c index 091d96f2d6829..40d587176a754 100644 --- a/drivers/gpio/gpio-mpc5200.c +++ b/drivers/gpio/gpio-mpc5200.c @@ -69,7 +69,7 @@ __mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) out_8(®s->wkup_dvo, chip->shadow_dvo); } -static void +static int mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; @@ -81,6 +81,8 @@ mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; } static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -151,7 +153,7 @@ static int mpc52xx_wkup_gpiochip_probe(struct platform_device *ofdev) gc->direction_input = mpc52xx_wkup_gpio_dir_in; gc->direction_output = mpc52xx_wkup_gpio_dir_out; gc->get = mpc52xx_wkup_gpio_get; - gc->set = mpc52xx_wkup_gpio_set; + gc->set_rv = mpc52xx_wkup_gpio_set; ret = of_mm_gpiochip_add_data(ofdev->dev.of_node, &chip->mmchip, chip); if (ret) @@ -228,7 +230,7 @@ __mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) out_be32(®s->simple_dvo, chip->shadow_dvo); } -static void +static int mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; @@ -240,6 +242,8 @@ mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; } static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) @@ -311,7 +315,7 @@ static int mpc52xx_simple_gpiochip_probe(struct platform_device *ofdev) gc->direction_input = mpc52xx_simple_gpio_dir_in; gc->direction_output = mpc52xx_simple_gpio_dir_out; gc->get = mpc52xx_simple_gpio_get; - gc->set = mpc52xx_simple_gpio_set; + gc->set_rv = mpc52xx_simple_gpio_set; ret = of_mm_gpiochip_add_data(ofdev->dev.of_node, &chip->mmchip, chip); if (ret) -- GitLab From 3aa3628f8168df9fe154b09b1710d3314b9fa4b7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:15 +0200 Subject: [PATCH 143/868] gpio: mpfs: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-5-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mpfs.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mpfs.c b/drivers/gpio/gpio-mpfs.c index 561a961c97a69..3415cb7ebb0f1 100644 --- a/drivers/gpio/gpio-mpfs.c +++ b/drivers/gpio/gpio-mpfs.c @@ -99,16 +99,19 @@ static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index) return regmap_test_bits(mpfs_gpio->regs, mpfs_gpio->offsets->inp, BIT(gpio_index)); } -static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) +static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) { struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int ret; mpfs_gpio_get(gc, gpio_index); - regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, BIT(gpio_index), - value << gpio_index); + ret = regmap_update_bits(mpfs_gpio->regs, mpfs_gpio->offsets->outp, + BIT(gpio_index), value << gpio_index); mpfs_gpio_get(gc, gpio_index); + + return ret; } static int mpfs_gpio_probe(struct platform_device *pdev) @@ -147,7 +150,7 @@ static int mpfs_gpio_probe(struct platform_device *pdev) mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output; mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction; mpfs_gpio->gc.get = mpfs_gpio_get; - mpfs_gpio->gc.set = mpfs_gpio_set; + mpfs_gpio->gc.set_rv = mpfs_gpio_set; mpfs_gpio->gc.base = -1; mpfs_gpio->gc.ngpio = ngpios; mpfs_gpio->gc.label = dev_name(dev); -- GitLab From e63d9fbe9f148b44f2fdc211941f2d4485022549 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:16 +0200 Subject: [PATCH 144/868] gpio: mpsse: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-6-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mpsse.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c index 3ea32c5e33d1a..b17de08e9e03c 100644 --- a/drivers/gpio/gpio-mpsse.c +++ b/drivers/gpio/gpio-mpsse.c @@ -160,8 +160,8 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank) return buf; } -static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { unsigned long i, bank, bank_mask, bank_bits; int ret; @@ -180,11 +180,11 @@ static void gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask, ret = gpio_mpsse_set_bank(priv, bank); if (ret) - dev_err(&priv->intf->dev, - "Couldn't set values for bank %ld!", - bank); + return ret; } } + + return 0; } static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask, @@ -227,7 +227,7 @@ static int gpio_mpsse_gpio_get(struct gpio_chip *chip, unsigned int offset) return 0; } -static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { unsigned long mask = 0, bits = 0; @@ -236,7 +236,7 @@ static void gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset, if (value) __set_bit(offset, &bits); - gpio_mpsse_set_multiple(chip, &mask, &bits); + return gpio_mpsse_set_multiple(chip, &mask, &bits); } static int gpio_mpsse_direction_output(struct gpio_chip *chip, @@ -249,9 +249,7 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip, scoped_guard(mutex, &priv->io_mutex) priv->gpio_dir[bank] |= BIT(bank_offset); - gpio_mpsse_gpio_set(chip, offset, value); - - return 0; + return gpio_mpsse_gpio_set(chip, offset, value); } static int gpio_mpsse_direction_input(struct gpio_chip *chip, @@ -450,9 +448,9 @@ static int gpio_mpsse_probe(struct usb_interface *interface, priv->gpio.direction_input = gpio_mpsse_direction_input; priv->gpio.direction_output = gpio_mpsse_direction_output; priv->gpio.get = gpio_mpsse_gpio_get; - priv->gpio.set = gpio_mpsse_gpio_set; + priv->gpio.set_rv = gpio_mpsse_gpio_set; priv->gpio.get_multiple = gpio_mpsse_get_multiple; - priv->gpio.set_multiple = gpio_mpsse_set_multiple; + priv->gpio.set_multiple_rv = gpio_mpsse_set_multiple; priv->gpio.base = -1; priv->gpio.ngpio = 16; priv->gpio.offset = priv->intf_id * priv->gpio.ngpio; -- GitLab From 88a775454a0fe923f3d34d8f30cd1d6b75be0859 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:17 +0200 Subject: [PATCH 145/868] gpio: msc313: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Daniel Palmer Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-7-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-msc313.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c index 6db9e469e0dc2..992339a89d198 100644 --- a/drivers/gpio/gpio-msc313.c +++ b/drivers/gpio/gpio-msc313.c @@ -486,7 +486,7 @@ struct msc313_gpio { u8 *saved; }; -static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct msc313_gpio *gpio = gpiochip_get_data(chip); u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); @@ -497,6 +497,8 @@ static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int val gpioreg &= ~MSC313_GPIO_OUT; writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; } static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -656,7 +658,7 @@ static int msc313_gpio_probe(struct platform_device *pdev) gpiochip->direction_input = msc313_gpio_direction_input; gpiochip->direction_output = msc313_gpio_direction_output; gpiochip->get = msc313_gpio_get; - gpiochip->set = msc313_gpio_set; + gpiochip->set_rv = msc313_gpio_set; gpiochip->base = -1; gpiochip->ngpio = gpio->gpio_data->num; gpiochip->names = gpio->gpio_data->names; -- GitLab From aaec273c7b511a7826df09123a1fd6e4896c1bfd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:18 +0200 Subject: [PATCH 146/868] gpio: nomadik: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-8-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-nomadik.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c index fa19a44943fd7..296d13845b300 100644 --- a/drivers/gpio/gpio-nomadik.c +++ b/drivers/gpio/gpio-nomadik.c @@ -347,8 +347,8 @@ static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned int offset) return value; } -static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, - int val) +static int nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, + int val) { struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip); @@ -357,6 +357,8 @@ static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned int offset, __nmk_gpio_set_output(nmk_chip, offset, val); clk_disable(nmk_chip->clk); + + return 0; } static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned int offset, @@ -672,7 +674,7 @@ static int nmk_gpio_probe(struct platform_device *pdev) chip->direction_input = nmk_gpio_make_input; chip->get = nmk_gpio_get_input; chip->direction_output = nmk_gpio_make_output; - chip->set = nmk_gpio_set_output; + chip->set_rv = nmk_gpio_set_output; chip->dbg_show = nmk_gpio_dbg_show; chip->can_sleep = false; chip->owner = THIS_MODULE; -- GitLab From 0e1a8930c941e3a7bea25928b254ece8caa5135d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:19 +0200 Subject: [PATCH 147/868] gpio: npcm-sgpio: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-9-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-npcm-sgpio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index 2605706145434..b3953d1ae8af4 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -226,7 +226,7 @@ static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) return GPIO_LINE_DIRECTION_IN; } -static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct npcm_sgpio *gpio = gpiochip_get_data(gc); const struct npcm_sgpio_bank *bank = offset_to_bank(offset); @@ -242,6 +242,8 @@ static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) reg &= ~BIT(GPIO_BIT(offset)); iowrite8(reg, addr); + + return 0; } static int npcm_sgpio_get(struct gpio_chip *gc, unsigned int offset) @@ -546,7 +548,7 @@ static int npcm_sgpio_probe(struct platform_device *pdev) gpio->chip.direction_output = npcm_sgpio_dir_out; gpio->chip.get_direction = npcm_sgpio_get_direction; gpio->chip.get = npcm_sgpio_get; - gpio->chip.set = npcm_sgpio_set; + gpio->chip.set_rv = npcm_sgpio_set; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; -- GitLab From f02614561493da22f24b0e2ec1c2ae0d5b41c68b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:20 +0200 Subject: [PATCH 148/868] gpio: octeon: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-10-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-octeon.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-octeon.c b/drivers/gpio/gpio-octeon.c index afb0e8a791e5a..24966161742a9 100644 --- a/drivers/gpio/gpio-octeon.c +++ b/drivers/gpio/gpio-octeon.c @@ -47,12 +47,15 @@ static int octeon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) return 0; } -static void octeon_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int octeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct octeon_gpio *gpio = gpiochip_get_data(chip); u64 mask = 1ull << offset; u64 reg = gpio->register_base + (value ? TX_SET : TX_CLEAR); cvmx_write_csr(reg, mask); + + return 0; } static int octeon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, @@ -105,7 +108,7 @@ static int octeon_gpio_probe(struct platform_device *pdev) chip->direction_input = octeon_gpio_dir_in; chip->get = octeon_gpio_get; chip->direction_output = octeon_gpio_dir_out; - chip->set = octeon_gpio_set; + chip->set_rv = octeon_gpio_set; err = devm_gpiochip_add_data(&pdev->dev, chip, gpio); if (err) return err; -- GitLab From 57065d62e672bce193f186c7b759f928b9a90ca0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:21 +0200 Subject: [PATCH 149/868] gpio: omap: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-11-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-omap.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 54c4bfdccf568..ed5c88a5c5207 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -953,7 +953,7 @@ static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset, return ret; } -static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int omap_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_bank *bank; unsigned long flags; @@ -962,10 +962,12 @@ static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value) raw_spin_lock_irqsave(&bank->lock, flags); bank->set_dataout(bank, offset, value); raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; } -static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { struct gpio_bank *bank = gpiochip_get_data(chip); void __iomem *reg = bank->base + bank->regs->dataout; @@ -977,6 +979,8 @@ static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, writel_relaxed(l, reg); bank->context.dataout = l; raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; } /*---------------------------------------------------------------------*/ @@ -1042,8 +1046,8 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev) bank->chip.get_multiple = omap_gpio_get_multiple; bank->chip.direction_output = omap_gpio_output; bank->chip.set_config = omap_gpio_set_config; - bank->chip.set = omap_gpio_set; - bank->chip.set_multiple = omap_gpio_set_multiple; + bank->chip.set_rv = omap_gpio_set; + bank->chip.set_multiple_rv = omap_gpio_set_multiple; if (bank->is_mpuio) { bank->chip.label = "mpuio"; if (bank->regs->wkup_en) -- GitLab From f3763403a6bbc3a18379fe4c415bda899a111d55 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 14:33:22 +0200 Subject: [PATCH 150/868] gpio: palmas: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-gpio-v1-12-3a9a3c1472ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-palmas.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 28dba7048509a..a076daee00658 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -54,12 +54,11 @@ static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & BIT(offset)); } -static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int palmas_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct palmas_gpio *pg = gpiochip_get_data(gc); struct palmas *palmas = pg->palmas; - int ret; unsigned int reg; int gpio16 = (offset/8); @@ -71,9 +70,7 @@ static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, reg = (value) ? PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT; - ret = palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); - if (ret < 0) - dev_err(gc->parent, "Reg 0x%02x write failed, %d\n", reg, ret); + return palmas_write(palmas, PALMAS_GPIO_BASE, reg, BIT(offset)); } static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -89,7 +86,9 @@ static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, reg = (gpio16) ? PALMAS_GPIO_DATA_DIR2 : PALMAS_GPIO_DATA_DIR; /* Set the initial value */ - palmas_gpio_set(gc, offset, value); + ret = palmas_gpio_set(gc, offset, value); + if (ret) + return ret; ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, reg, BIT(offset), BIT(offset)); @@ -166,7 +165,7 @@ static int palmas_gpio_probe(struct platform_device *pdev) palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; - palmas_gpio->gpio_chip.set = palmas_gpio_set; + palmas_gpio->gpio_chip.set_rv = palmas_gpio_set; palmas_gpio->gpio_chip.get = palmas_gpio_get; palmas_gpio->gpio_chip.parent = &pdev->dev; -- GitLab From d9f38d9824bfb1b046d2e720349d2f45959ab184 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 17 Jun 2025 11:21:03 +0800 Subject: [PATCH 151/868] ASoC: tegra: AHUB: Remove unneeded semicolon Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Link: https://patch.msgid.link/20250617032103.1725040-1-nichen@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/tegra/tegra210_ahub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 2376cc76e6842..21aeaeba0b107 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -2066,7 +2066,7 @@ static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg) return true; default: break; - }; + } } return false; -- GitLab From 3e1c01d06e1f52f78fe00ef26a9cf80dbb0a3115 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 16 Jun 2025 17:40:07 +0200 Subject: [PATCH 152/868] regulator: rpi-panel-v2: Add shutdown hook Add shutdown hook so that the panel gets powered off with the system. Signed-off-by: Dave Stevenson Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250616154018.430004-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/rpi-panel-v2-regulator.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c index c506fd699d572..30b78aa75ee38 100644 --- a/drivers/regulator/rpi-panel-v2-regulator.c +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -89,9 +89,19 @@ static int rpi_panel_v2_i2c_probe(struct i2c_client *i2c) if (ret) return dev_err_probe(&i2c->dev, ret, "Failed to create gpiochip\n"); + i2c_set_clientdata(i2c, regmap); + return devm_pwmchip_add(&i2c->dev, pc); } +static void rpi_panel_v2_i2c_shutdown(struct i2c_client *client) +{ + struct regmap *regmap = i2c_get_clientdata(client); + + regmap_write(regmap, REG_PWM, 0); + regmap_write(regmap, REG_POWERON, 0); +} + static const struct of_device_id rpi_panel_v2_dt_ids[] = { { .compatible = "raspberrypi,touchscreen-panel-regulator-v2" }, { }, @@ -105,6 +115,7 @@ static struct i2c_driver rpi_panel_v2_regulator_driver = { .of_match_table = rpi_panel_v2_dt_ids, }, .probe = rpi_panel_v2_i2c_probe, + .shutdown = rpi_panel_v2_i2c_shutdown, }; module_i2c_driver(rpi_panel_v2_regulator_driver); -- GitLab From 7f4c540e0859e2025675d2c5c5c6ab88eaf817e2 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Mon, 16 Jun 2025 15:43:31 +0800 Subject: [PATCH 153/868] ALSA: hda/conexant: Renaming the codec with device ID 0x1f86 and 0x1f87 Due to changes in the manufacturer's plan, all 0x14f11f86 will be named CX11880, and 0x14f11f87 will be named SN6140 Signed-off-by: wangdicheng Link: https://patch.msgid.link/20250616074331.581309-1-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 34874039ad453..e584a7b2877de 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -42,7 +42,7 @@ struct conexant_spec { unsigned int gpio_led; unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; - bool is_cx8070_sn6140; + bool is_cx11880_sn6140; }; @@ -195,7 +195,7 @@ static int cx_auto_init(struct hda_codec *codec) cxt_init_gpio_led(codec); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - if (spec->is_cx8070_sn6140) + if (spec->is_cx11880_sn6140) cx_fixup_headset_recog(codec); return 0; @@ -247,7 +247,7 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ { unsigned int mic_present; - /* In cx8070 and sn6140, the node 16 can only be configured to headphone or disabled, + /* In cx11880 and sn6140, the node 16 can only be configured to headphone or disabled, * the node 19 can only be configured to microphone or disabled. * Check hp&mic tag to process headset plugin & plugout. */ @@ -1192,11 +1192,11 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->spec = spec; codec->patch_ops = cx_auto_patch_ops; - /* init cx8070/sn6140 flag and reset headset_present_flag */ + /* init cx11880/sn6140 flag and reset headset_present_flag */ switch (codec->core.vendor_id) { case 0x14f11f86: case 0x14f11f87: - spec->is_cx8070_sn6140 = true; + spec->is_cx11880_sn6140 = true; snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref); break; } @@ -1284,7 +1284,7 @@ static int patch_conexant_auto(struct hda_codec *codec) */ static const struct hda_device_id snd_hda_id_conexant[] = { - HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto), + HDA_CODEC_ENTRY(0x14f11f86, "CX11880", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto), -- GitLab From 7b20980ffc11514d8849811857d915001236bcfa Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 9 Jun 2025 17:08:59 -0500 Subject: [PATCH 154/868] dt-bindings: gpio: arm,pl061: Drop interrupt properties as required It is possible that the PL061 doesn't have any interrupt connected and can't be an interrupt provider, so drop the interrupt properties as required. The LG LG131x SoCs are one example of this. Signed-off-by: Rob Herring (Arm) Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20250609220900.3035642-1-robh@kernel.org [Bartosz: g/pl011/pl061/] Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/pl061-gpio.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml index bd35cbf7fa09c..c51e10680c0a5 100644 --- a/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/pl061-gpio.yaml @@ -60,9 +60,6 @@ properties: required: - compatible - reg - - interrupts - - interrupt-controller - - "#interrupt-cells" - clocks - "#gpio-cells" - gpio-controller -- GitLab From 7f8924e8785b68c998bc1906e049bf5595865e60 Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Tue, 17 Jun 2025 10:46:19 -0400 Subject: [PATCH 155/868] ASoC: dt-bindings: cirrus,cs42xx8: add 'port' property The cs42xx8 codecs may be used with audio graph card and thus may require an additional property: 'port'. Add it. Signed-off-by: Laurentiu Mihalcea Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250617144619.1130857-1-laurentiumihalcea111@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml index 725b47e82062a..cd47905eb20a7 100644 --- a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml @@ -41,6 +41,10 @@ properties: description: This pin is connected to the chip's RESET pin. maxItems: 1 + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + required: - compatible - reg -- GitLab From 5fc2c383125c2b4b6037e02ad8796b776b25e6d0 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Wed, 18 Jun 2025 22:53:29 +0800 Subject: [PATCH 156/868] spi: falcon: mark falcon_sflash_xfer() as static Fix the following missing-prototypes build warning: drivers/spi/spi-falcon.c:97:5: error: no previous prototype for 'falcon_sflash_xfer' [-Werror=missing-prototypes] 97 | int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, | ^~~~~~~~~~~~~~~~~~ Signed-off-by: Shiji Yang Link: https://patch.msgid.link/OSBPR01MB16705BE87E549B6210CD6BCABC72A@OSBPR01MB1670.jpnprd01.prod.outlook.com Signed-off-by: Mark Brown --- drivers/spi/spi-falcon.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 84279058f0f17..faa893f83dc53 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -94,8 +94,9 @@ struct falcon_sflash { struct spi_controller *host; }; -int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, - unsigned long flags) +static int +falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t, + unsigned long flags) { struct device *dev = &spi->dev; struct falcon_sflash *priv = spi_controller_get_devdata(spi->controller); -- GitLab From 1a35c88302a3b2827ec47f0b2d0530b543938fb3 Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:20 -0700 Subject: [PATCH 157/868] ACPI: APEI: EINJ: Fix kernel test sparse warnings This patch fixes the kernel test robot warning reported here: Link: https://lore.kernel.org/all/202410241620.oApALow5-lkp@intel.com/ Use pointers annotated with the __iomem marker for all iomem map calls, and creates a local copy of the mapped IO memory for future access in the code. memcpy_fromio() and memcpy_toio() are used to read/write data from/to mapped IO memory. Reviewed-by: Ira Weiny Reviewed-by: Jonathan Cameron Reviewed-by: Tony Luck Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-2-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 106 +++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 9b041415a9d01..e4fb4405deaee 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -151,7 +151,7 @@ static DEFINE_MUTEX(einj_mutex); */ bool einj_initialized __ro_after_init; -static void *einj_param; +static void __iomem *einj_param; static void einj_exec_ctx_init(struct apei_exec_context *ctx) { @@ -216,24 +216,26 @@ static void check_vendor_extension(u64 paddr, struct set_error_type_with_address *v5param) { int offset = v5param->vendor_extension; - struct vendor_error_type_extension *v; + struct vendor_error_type_extension v; + struct vendor_error_type_extension __iomem *p; u32 sbdf; if (!offset) return; - v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); - if (!v) + p = acpi_os_map_iomem(paddr + offset, sizeof(*p)); + if (!p) return; - get_oem_vendor_struct(paddr, offset, v); - sbdf = v->pcie_sbdf; + memcpy_fromio(&v, p, sizeof(v)); + get_oem_vendor_struct(paddr, offset, &v); + sbdf = v.pcie_sbdf; sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", sbdf >> 24, (sbdf >> 16) & 0xff, (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, - v->vendor_id, v->device_id, v->rev_id); - acpi_os_unmap_iomem(v, sizeof(*v)); + v.vendor_id, v.device_id, v.rev_id); + acpi_os_unmap_iomem(p, sizeof(v)); } -static void *einj_get_parameter_address(void) +static void __iomem *einj_get_parameter_address(void) { int i; u64 pa_v4 = 0, pa_v5 = 0; @@ -254,26 +256,30 @@ static void *einj_get_parameter_address(void) entry++; } if (pa_v5) { - struct set_error_type_with_address *v5param; + struct set_error_type_with_address v5param; + struct set_error_type_with_address __iomem *p; - v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param)); - if (v5param) { + p = acpi_os_map_iomem(pa_v5, sizeof(*p)); + if (p) { + memcpy_fromio(&v5param, p, sizeof(v5param)); acpi5 = 1; - check_vendor_extension(pa_v5, v5param); - return v5param; + check_vendor_extension(pa_v5, &v5param); + return p; } } if (param_extension && pa_v4) { - struct einj_parameter *v4param; + struct einj_parameter v4param; + struct einj_parameter __iomem *p; - v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param)); - if (!v4param) + p = acpi_os_map_iomem(pa_v4, sizeof(*p)); + if (!p) return NULL; - if (v4param->reserved1 || v4param->reserved2) { - acpi_os_unmap_iomem(v4param, sizeof(*v4param)); + memcpy_fromio(&v4param, p, sizeof(v4param)); + if (v4param.reserved1 || v4param.reserved2) { + acpi_os_unmap_iomem(p, sizeof(v4param)); return NULL; } - return v4param; + return p; } return NULL; @@ -319,7 +325,7 @@ static struct acpi_generic_address *einj_get_trigger_parameter_region( static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) { - struct acpi_einj_trigger *trigger_tab = NULL; + struct acpi_einj_trigger trigger_tab; struct apei_exec_context trigger_ctx; struct apei_resources trigger_resources; struct acpi_whea_header *trigger_entry; @@ -327,54 +333,57 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, u32 table_size; int rc = -EIO; struct acpi_generic_address *trigger_param_region = NULL; + struct acpi_einj_trigger __iomem *p; - r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), + r = request_mem_region(trigger_paddr, sizeof(trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr + - sizeof(*trigger_tab) - 1); + sizeof(trigger_tab) - 1); goto out; } - trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); - if (!trigger_tab) { + p = ioremap_cache(trigger_paddr, sizeof(*p)); + if (!p) { pr_err("Failed to map trigger table!\n"); goto out_rel_header; } - rc = einj_check_trigger_header(trigger_tab); + memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab)); + rc = einj_check_trigger_header(&trigger_tab); if (rc) { pr_warn(FW_BUG "Invalid trigger error action table.\n"); goto out_rel_header; } /* No action structures in the TRIGGER_ERROR table, nothing to do */ - if (!trigger_tab->entry_count) + if (!trigger_tab.entry_count) goto out_rel_header; rc = -EIO; - table_size = trigger_tab->table_size; - r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), - table_size - sizeof(*trigger_tab), + table_size = trigger_tab.table_size; + r = request_mem_region(trigger_paddr + sizeof(trigger_tab), + table_size - sizeof(trigger_tab), "APEI EINJ Trigger Table"); if (!r) { pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", - (unsigned long long)trigger_paddr + sizeof(*trigger_tab), + (unsigned long long)trigger_paddr + sizeof(trigger_tab), (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; } - iounmap(trigger_tab); - trigger_tab = ioremap_cache(trigger_paddr, table_size); - if (!trigger_tab) { + iounmap(p); + p = ioremap_cache(trigger_paddr, table_size); + if (!p) { pr_err("Failed to map trigger table!\n"); goto out_rel_entry; } + memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab)); trigger_entry = (struct acpi_whea_header *) - ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); + ((char *)&trigger_tab + sizeof(struct acpi_einj_trigger)); apei_resources_init(&trigger_resources); apei_exec_ctx_init(&trigger_ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), - trigger_entry, trigger_tab->entry_count); + trigger_entry, trigger_tab.entry_count); rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources); if (rc) goto out_fini; @@ -392,7 +401,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, apei_resources_init(&addr_resources); trigger_param_region = einj_get_trigger_parameter_region( - trigger_tab, param1, param2); + &trigger_tab, param1, param2); if (trigger_param_region) { rc = apei_resources_add(&addr_resources, trigger_param_region->address, @@ -421,13 +430,13 @@ out_release: out_fini: apei_resources_fini(&trigger_resources); out_rel_entry: - release_mem_region(trigger_paddr + sizeof(*trigger_tab), - table_size - sizeof(*trigger_tab)); + release_mem_region(trigger_paddr + sizeof(trigger_tab), + table_size - sizeof(trigger_tab)); out_rel_header: - release_mem_region(trigger_paddr, sizeof(*trigger_tab)); + release_mem_region(trigger_paddr, sizeof(trigger_tab)); out: - if (trigger_tab) - iounmap(trigger_tab); + if (p) + iounmap(p); return rc; } @@ -446,8 +455,10 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, return rc; apei_exec_ctx_set_input(&ctx, type); if (acpi5) { - struct set_error_type_with_address *v5param = einj_param; + struct set_error_type_with_address *v5param, v5_struct; + v5param = &v5_struct; + memcpy_fromio(v5param, einj_param, sizeof(*v5param)); v5param->type = type; if (type & ACPI5_VENDOR_BIT) { switch (vendor_flags) { @@ -492,15 +503,18 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, break; } } + memcpy_toio(einj_param, v5param, sizeof(*v5param)); } else { rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); if (rc) return rc; if (einj_param) { - struct einj_parameter *v4param = einj_param; + struct einj_parameter v4param; - v4param->param1 = param1; - v4param->param2 = param2; + memcpy_fromio(&v4param, einj_param, sizeof(v4param)); + v4param.param1 = param1; + v4param.param2 = param2; + memcpy_toio(einj_param, &v4param, sizeof(v4param)); } } rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); -- GitLab From 0c6176e1e1862fd09484c50de17c04b3ca388c22 Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:21 -0700 Subject: [PATCH 158/868] ACPI: APEI: EINJ: Enable the discovery of EINJv2 capabilities Enable the driver to show all supported error injections for EINJ and EINJv2 at the same time. EINJv2 capabilities can be discovered by checking the return value of get_error_type, where bit 30 set indicates EINJv2 support. Reviewed-by: Jonathan Cameron Reviewed-by: Tony Luck Reviewed-by: Ira Weiny Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-3-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/apei-internal.h | 2 +- drivers/acpi/apei/einj-core.c | 75 +++++++++++++++++++++++++------ drivers/acpi/apei/einj-cxl.c | 2 +- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index cd2766c69d78d..77c10a7a7a9f4 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -131,7 +131,7 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus) int apei_osc_setup(void); -int einj_get_available_error_type(u32 *type); +int einj_get_available_error_type(u32 *type, int einj_action); int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 param4); int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2, diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index e4fb4405deaee..a1ff42c226fba 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -33,6 +33,7 @@ #define SLEEP_UNIT_MAX 5000 /* 5ms */ /* Firmware should respond within 1 seconds */ #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC) +#define ACPI65_EINJV2_SUPP BIT(30) #define ACPI5_VENDOR_BIT BIT(31) #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ ACPI_EINJ_MEMORY_UNCORRECTABLE | \ @@ -84,6 +85,7 @@ static struct debugfs_blob_wrapper vendor_errors; static char vendor_dev[64]; static u32 available_error_type; +static u32 available_error_type_v2; /* * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the @@ -159,13 +161,13 @@ static void einj_exec_ctx_init(struct apei_exec_context *ctx) EINJ_TAB_ENTRY(einj_tab), einj_tab->entries); } -static int __einj_get_available_error_type(u32 *type) +static int __einj_get_available_error_type(u32 *type, int einj_action) { struct apei_exec_context ctx; int rc; einj_exec_ctx_init(&ctx); - rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE); + rc = apei_exec_run(&ctx, einj_action); if (rc) return rc; *type = apei_exec_ctx_get_output(&ctx); @@ -174,17 +176,34 @@ static int __einj_get_available_error_type(u32 *type) } /* Get error injection capabilities of the platform */ -int einj_get_available_error_type(u32 *type) +int einj_get_available_error_type(u32 *type, int einj_action) { int rc; mutex_lock(&einj_mutex); - rc = __einj_get_available_error_type(type); + rc = __einj_get_available_error_type(type, einj_action); mutex_unlock(&einj_mutex); return rc; } +static int einj_get_available_error_types(u32 *type1, u32 *type2) +{ + int rc; + + rc = einj_get_available_error_type(type1, ACPI_EINJ_GET_ERROR_TYPE); + if (rc) + return rc; + if (*type1 & ACPI65_EINJV2_SUPP) { + rc = einj_get_available_error_type(type2, + ACPI_EINJV2_GET_ERROR_TYPE); + if (rc) + return rc; + } + + return 0; +} + static int einj_timedout(u64 *t) { if ((s64)*t < SLEEP_UNIT_MIN) { @@ -646,6 +665,7 @@ static u64 error_param2; static u64 error_param3; static u64 error_param4; static struct dentry *einj_debug_dir; +static char einj_buf[32]; static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(0), "Processor Correctable" }, { BIT(1), "Processor Uncorrectable non-fatal" }, @@ -662,6 +682,12 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(31), "Vendor Defined Error Types" }, }; +static struct { u32 mask; const char *str; } const einjv2_error_type_string[] = { + { BIT(0), "EINJV2 Processor Error" }, + { BIT(1), "EINJV2 Memory Error" }, + { BIT(2), "EINJV2 PCI Express Error" }, +}; + static int available_error_type_show(struct seq_file *m, void *v) { @@ -669,17 +695,22 @@ static int available_error_type_show(struct seq_file *m, void *v) if (available_error_type & einj_error_type_string[pos].mask) seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask, einj_error_type_string[pos].str); - + if (available_error_type & ACPI65_EINJV2_SUPP) { + for (int pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++) { + if (available_error_type_v2 & einjv2_error_type_string[pos].mask) + seq_printf(m, "V2_0x%08x\t%s\n", einjv2_error_type_string[pos].mask, + einjv2_error_type_string[pos].str); + } + } return 0; } DEFINE_SHOW_ATTRIBUTE(available_error_type); -static int error_type_get(void *data, u64 *val) +static ssize_t error_type_get(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { - *val = error_type; - - return 0; + return simple_read_from_buffer(buf, count, ppos, einj_buf, strlen(einj_buf)); } bool einj_is_cxl_error_type(u64 type) @@ -712,9 +743,23 @@ int einj_validate_error_type(u64 type) return 0; } -static int error_type_set(void *data, u64 val) +static ssize_t error_type_set(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { int rc; + u64 val; + + memset(einj_buf, 0, sizeof(einj_buf)); + if (copy_from_user(einj_buf, buf, count)) + return -EFAULT; + + if (strncmp(einj_buf, "V2_", 3) == 0) { + if (!sscanf(einj_buf, "V2_%llx", &val)) + return -EINVAL; + } else { + if (!sscanf(einj_buf, "%llx", &val)) + return -EINVAL; + } rc = einj_validate_error_type(val); if (rc) @@ -722,11 +767,13 @@ static int error_type_set(void *data, u64 val) error_type = val; - return 0; + return count; } -DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set, - "0x%llx\n"); +static const struct file_operations error_type_fops = { + .read = error_type_get, + .write = error_type_set, +}; static int error_inject_set(void *data, u64 val) { @@ -778,7 +825,7 @@ static int __init einj_probe(struct faux_device *fdev) goto err_put_table; } - rc = einj_get_available_error_type(&available_error_type); + rc = einj_get_available_error_types(&available_error_type, &available_error_type_v2); if (rc) goto err_put_table; diff --git a/drivers/acpi/apei/einj-cxl.c b/drivers/acpi/apei/einj-cxl.c index 78da9ae543a2b..e70a416ec9256 100644 --- a/drivers/acpi/apei/einj-cxl.c +++ b/drivers/acpi/apei/einj-cxl.c @@ -30,7 +30,7 @@ int einj_cxl_available_error_type_show(struct seq_file *m, void *v) int cxl_err, rc; u32 available_error_type = 0; - rc = einj_get_available_error_type(&available_error_type); + rc = einj_get_available_error_type(&available_error_type, ACPI_EINJ_GET_ERROR_TYPE); if (rc) return rc; -- GitLab From 21cd921b1a5a4c8d0097343bda7e6e78d21e9773 Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:22 -0700 Subject: [PATCH 159/868] ACPI: APEI: EINJ: Add einjv2 extension struct Add einjv2 extension struct and EINJv2 error types to prepare the driver for EINJv2 support. ACPI specifications[1] enables EINJv2 by extending set_error_type_with_address struct. Link: https://uefi.org/specs/ACPI/6.6/18_Platform_Error_Interfaces.html#einjv2-extension-structure [1] Reviewed-by: Jonathan Cameron Reviewed-by: Tony Luck Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-4-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index a1ff42c226fba..1ffe8270634c2 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -33,6 +33,7 @@ #define SLEEP_UNIT_MAX 5000 /* 5ms */ /* Firmware should respond within 1 seconds */ #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC) +#define COMPONENT_LEN 16 #define ACPI65_EINJV2_SUPP BIT(30) #define ACPI5_VENDOR_BIT BIT(31) #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ @@ -50,6 +51,28 @@ */ static int acpi5; +struct syndrome_array { + union { + u8 acpi_id[COMPONENT_LEN]; + u8 device_id[COMPONENT_LEN]; + u8 pcie_sbdf[COMPONENT_LEN]; + u8 vendor_id[COMPONENT_LEN]; + } comp_id; + union { + u8 proc_synd[COMPONENT_LEN]; + u8 mem_synd[COMPONENT_LEN]; + u8 pcie_synd[COMPONENT_LEN]; + u8 vendor_synd[COMPONENT_LEN]; + } comp_synd; +}; + +struct einjv2_extension_struct { + u32 length; + u16 revision; + u16 component_arr_count; + struct syndrome_array component_arr[] __counted_by(component_arr_count); +}; + struct set_error_type_with_address { u32 type; u32 vendor_extension; @@ -58,6 +81,7 @@ struct set_error_type_with_address { u64 memory_address; u64 memory_address_range; u32 pcie_sbdf; + struct einjv2_extension_struct einjv2_struct; }; enum { SETWA_FLAGS_APICID = 1, -- GitLab From 691a0f0a557b19316ef533f9ca34c72ab6c7ae56 Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:23 -0700 Subject: [PATCH 160/868] ACPI: APEI: EINJ: Discover EINJv2 parameters The EINJv2 set_error_type_with_address structure has a flex array to hold the component IDs and syndrome values used when injecting multiple errors at once. Discover the size of this array by taking the address from the ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS entry in the EINJ table and reading the BIOS copy of the structure. Derive the maximum number of components from the length field in the einjv2_extension_struct at the end of the BIOS copy. Map the whole of the structure into kernel memory (and unmap on module unload). [Tony: Code unchanged from Zaid's original. New commit message] Reviewed-by: Tony Luck Reviewed-by: Ira Weiny Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-5-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 1ffe8270634c2..ea6fd4343e634 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -108,6 +108,7 @@ static struct debugfs_blob_wrapper vendor_blob; static struct debugfs_blob_wrapper vendor_errors; static char vendor_dev[64]; +static u32 max_nr_components; static u32 available_error_type; static u32 available_error_type_v2; @@ -178,6 +179,7 @@ static DEFINE_MUTEX(einj_mutex); bool einj_initialized __ro_after_init; static void __iomem *einj_param; +static u32 v5param_size; static void einj_exec_ctx_init(struct apei_exec_context *ctx) { @@ -302,11 +304,31 @@ static void __iomem *einj_get_parameter_address(void) struct set_error_type_with_address v5param; struct set_error_type_with_address __iomem *p; + v5param_size = sizeof(v5param); p = acpi_os_map_iomem(pa_v5, sizeof(*p)); if (p) { - memcpy_fromio(&v5param, p, sizeof(v5param)); + int offset, len; + + memcpy_fromio(&v5param, p, v5param_size); acpi5 = 1; check_vendor_extension(pa_v5, &v5param); + if (available_error_type & ACPI65_EINJV2_SUPP) { + len = v5param.einjv2_struct.length; + offset = offsetof(struct einjv2_extension_struct, component_arr); + max_nr_components = (len - offset) / + sizeof(v5param.einjv2_struct.component_arr[0]); + /* + * The first call to acpi_os_map_iomem above does not include the + * component array, instead it is used to read and calculate maximum + * number of components supported by the system. Below, the mapping + * is expanded to include the component array. + */ + acpi_os_unmap_iomem(p, v5param_size); + offset = offsetof(struct set_error_type_with_address, einjv2_struct); + v5param_size = offset + struct_size(&v5param.einjv2_struct, + component_arr, max_nr_components); + p = acpi_os_map_iomem(pa_v5, v5param_size); + } return p; } } @@ -933,7 +955,7 @@ static void __exit einj_remove(struct faux_device *fdev) if (einj_param) { acpi_size size = (acpi5) ? - sizeof(struct set_error_type_with_address) : + v5param_size : sizeof(struct einj_parameter); acpi_os_unmap_iomem(einj_param, size); -- GitLab From 90711f7bdf76faa9ed766236bc1f1fbd4364b5e7 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 17 Jun 2025 12:30:24 -0700 Subject: [PATCH 161/868] ACPI: APEI: EINJ: Create debugfs files to enter device id and syndrome EINJv2 allows users to inject multiple errors at the same time by specifying the device id and syndrome bits for each error in a flex array. Create files in the einj debugfs directory to enter data for each device id and syndrome value. Note that the specification says these are 128-bit little-endian values. Linux doesn't have a handy helper to manage objects of this type. Signed-off-by: Tony Luck Reviewed-by: Ira Weiny Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-6-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 97 +++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index ea6fd4343e634..87f1b8718387d 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -111,6 +111,7 @@ static char vendor_dev[64]; static u32 max_nr_components; static u32 available_error_type; static u32 available_error_type_v2; +static struct syndrome_array *syndrome_data; /* * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the @@ -712,6 +713,7 @@ static u64 error_param3; static u64 error_param4; static struct dentry *einj_debug_dir; static char einj_buf[32]; +static bool einj_v2_enabled; static struct { u32 mask; const char *str; } const einj_error_type_string[] = { { BIT(0), "Processor Correctable" }, { BIT(1), "Processor Uncorrectable non-fatal" }, @@ -848,6 +850,98 @@ static int einj_check_table(struct acpi_table_einj *einj_tab) return 0; } +static ssize_t u128_read(struct file *f, char __user *buf, size_t count, loff_t *off) +{ + char output[2 * COMPONENT_LEN + 1]; + u8 *data = f->f_inode->i_private; + int i; + + if (*off >= sizeof(output)) + return 0; + + for (i = 0; i < COMPONENT_LEN; i++) + sprintf(output + 2 * i, "%.02x", data[COMPONENT_LEN - i - 1]); + output[2 * COMPONENT_LEN] = '\n'; + + return simple_read_from_buffer(buf, count, off, output, sizeof(output)); +} + +static ssize_t u128_write(struct file *f, const char __user *buf, size_t count, loff_t *off) +{ + char input[2 + 2 * COMPONENT_LEN + 2]; + u8 *save = f->f_inode->i_private; + u8 tmp[COMPONENT_LEN]; + char byte[3] = {}; + char *s, *e; + size_t c; + long val; + int i; + + /* Require that user supply whole input line in one write(2) syscall */ + if (*off) + return -EINVAL; + + c = simple_write_to_buffer(input, sizeof(input), off, buf, count); + if (c < 0) + return c; + + if (c < 1 || input[c - 1] != '\n') + return -EINVAL; + + /* Empty line means invalidate this entry */ + if (c == 1) { + memset(save, 0xff, COMPONENT_LEN); + return c; + } + + if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')) + s = input + 2; + else + s = input; + e = input + c - 1; + + for (i = 0; i < COMPONENT_LEN; i++) { + byte[1] = *--e; + byte[0] = e > s ? *--e : '0'; + if (kstrtol(byte, 16, &val)) + return -EINVAL; + tmp[i] = val; + if (e <= s) + break; + } + while (++i < COMPONENT_LEN) + tmp[i] = 0; + + memcpy(save, tmp, COMPONENT_LEN); + + return c; +} + +static const struct file_operations u128_fops = { + .read = u128_read, + .write = u128_write, +}; + +static bool setup_einjv2_component_files(void) +{ + char name[32]; + + syndrome_data = kcalloc(max_nr_components, sizeof(syndrome_data[0]), GFP_KERNEL); + if (!syndrome_data) + return false; + + for (int i = 0; i < max_nr_components; i++) { + sprintf(name, "component_id%d", i); + debugfs_create_file(name, 0600, einj_debug_dir, + &syndrome_data[i].comp_id, &u128_fops); + sprintf(name, "component_syndrome%d", i); + debugfs_create_file(name, 0600, einj_debug_dir, + &syndrome_data[i].comp_synd, &u128_fops); + } + + return true; +} + static int __init einj_probe(struct faux_device *fdev) { int rc; @@ -919,6 +1013,8 @@ static int __init einj_probe(struct faux_device *fdev) &error_param4); debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, einj_debug_dir, ¬rigger); + if (available_error_type & ACPI65_EINJV2_SUPP) + einj_v2_enabled = setup_einjv2_component_files(); } if (vendor_dev[0]) { @@ -967,6 +1063,7 @@ static void __exit einj_remove(struct faux_device *fdev) apei_resources_release(&einj_resources); apei_resources_fini(&einj_resources); debugfs_remove_recursive(einj_debug_dir); + kfree(syndrome_data); acpi_put_table((struct acpi_table_header *)einj_tab); } -- GitLab From b47610296d17f90ccb24085dcd75fd083acc736d Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:25 -0700 Subject: [PATCH 162/868] ACPI: APEI: EINJ: Enable EINJv2 error injections Enable injection using EINJv2 mode of operation. [Tony: Mostly Zaid's original code. I just changed how the error ID and syndrome bits are implemented. Also swapped out some camelcase variable names] Co-developed-by: Tony Luck Signed-off-by: Tony Luck Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-7-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 58 ++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 87f1b8718387d..d6d7e36e36479 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -87,6 +87,7 @@ enum { SETWA_FLAGS_APICID = 1, SETWA_FLAGS_MEM = 2, SETWA_FLAGS_PCIE_SBDF = 4, + SETWA_FLAGS_EINJV2 = 8, }; /* @@ -181,6 +182,7 @@ bool einj_initialized __ro_after_init; static void __iomem *einj_param; static u32 v5param_size; +static bool is_v2; static void einj_exec_ctx_init(struct apei_exec_context *ctx) { @@ -507,12 +509,20 @@ out: return rc; } +static bool is_end_of_list(u8 *val) +{ + for (int i = 0; i < COMPONENT_LEN; ++i) { + if (val[i] != 0xFF) + return false; + } + return true; +} static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 param4) { struct apei_exec_context ctx; u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; - int rc; + int i, rc; einj_exec_ctx_init(&ctx); @@ -521,10 +531,10 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, return rc; apei_exec_ctx_set_input(&ctx, type); if (acpi5) { - struct set_error_type_with_address *v5param, v5_struct; + struct set_error_type_with_address *v5param; - v5param = &v5_struct; - memcpy_fromio(v5param, einj_param, sizeof(*v5param)); + v5param = kmalloc(v5param_size, GFP_KERNEL); + memcpy_fromio(v5param, einj_param, v5param_size); v5param->type = type; if (type & ACPI5_VENDOR_BIT) { switch (vendor_flags) { @@ -544,8 +554,21 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, v5param->flags = flags; v5param->memory_address = param1; v5param->memory_address_range = param2; - v5param->apicid = param3; - v5param->pcie_sbdf = param4; + + if (is_v2) { + for (i = 0; i < max_nr_components; i++) { + if (is_end_of_list(syndrome_data[i].comp_id.acpi_id)) + break; + v5param->einjv2_struct.component_arr[i].comp_id = + syndrome_data[i].comp_id; + v5param->einjv2_struct.component_arr[i].comp_synd = + syndrome_data[i].comp_synd; + } + v5param->einjv2_struct.component_arr_count = i; + } else { + v5param->apicid = param3; + v5param->pcie_sbdf = param4; + } } else { switch (type) { case ACPI_EINJ_PROCESSOR_CORRECTABLE: @@ -569,7 +592,8 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, break; } } - memcpy_toio(einj_param, v5param, sizeof(*v5param)); + memcpy_toio(einj_param, v5param, v5param_size); + kfree(v5param); } else { rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); if (rc) @@ -631,10 +655,15 @@ int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, u64 base_addr, size; /* If user manually set "flags", make sure it is legal */ - if (flags && (flags & - ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) + if (flags && (flags & ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM | + SETWA_FLAGS_PCIE_SBDF | SETWA_FLAGS_EINJV2))) return -EINVAL; + /* check if type is a valid EINJv2 error type */ + if (is_v2) { + if (!(type & available_error_type_v2)) + return -EINVAL; + } /* * We need extra sanity checks for memory errors. * Other types leap directly to injection. @@ -743,7 +772,7 @@ static int available_error_type_show(struct seq_file *m, void *v) if (available_error_type & einj_error_type_string[pos].mask) seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask, einj_error_type_string[pos].str); - if (available_error_type & ACPI65_EINJV2_SUPP) { + if ((available_error_type & ACPI65_EINJV2_SUPP) && einj_v2_enabled) { for (int pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++) { if (available_error_type_v2 & einjv2_error_type_string[pos].mask) seq_printf(m, "V2_0x%08x\t%s\n", einjv2_error_type_string[pos].mask, @@ -785,7 +814,7 @@ int einj_validate_error_type(u64 type) if (tval & (tval - 1)) return -EINVAL; if (!vendor) - if (!(type & available_error_type)) + if (!(type & (available_error_type | available_error_type_v2))) return -EINVAL; return 0; @@ -804,9 +833,11 @@ static ssize_t error_type_set(struct file *file, const char __user *buf, if (strncmp(einj_buf, "V2_", 3) == 0) { if (!sscanf(einj_buf, "V2_%llx", &val)) return -EINVAL; + is_v2 = true; } else { if (!sscanf(einj_buf, "%llx", &val)) return -EINVAL; + is_v2 = false; } rc = einj_validate_error_type(val); @@ -828,6 +859,11 @@ static int error_inject_set(void *data, u64 val) if (!error_type) return -EINVAL; + if (is_v2) + error_flags |= SETWA_FLAGS_EINJV2; + else + error_flags &= ~SETWA_FLAGS_EINJV2; + return einj_error_inject(error_type, error_flags, error_param1, error_param2, error_param3, error_param4); } -- GitLab From 8b148a97931db247771c43a0b5abdc31936c35f0 Mon Sep 17 00:00:00 2001 From: Zaid Alali Date: Tue, 17 Jun 2025 12:30:26 -0700 Subject: [PATCH 163/868] ACPI: APEI: EINJ: Update the documentation for EINJv2 support Add documentation based on implementation of EINJv2 as described in ACPI 6.5.A specification. [Tony: New user interface for device id and syndrome] Link: https://uefi.org/specs/ACPI/6.5_A/18_Platform_Error_Interfaces.html#error-injection Co-developed-by: Tony Luck Signed-off-by: Tony Luck Signed-off-by: Zaid Alali Link: https://patch.msgid.link/20250617193026.637510-8-zaidal@os.amperecomputing.com Signed-off-by: Rafael J. Wysocki --- .../firmware-guide/acpi/apei/einj.rst | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Documentation/firmware-guide/acpi/apei/einj.rst b/Documentation/firmware-guide/acpi/apei/einj.rst index c52b9da08fa9a..7d8435d35a18b 100644 --- a/Documentation/firmware-guide/acpi/apei/einj.rst +++ b/Documentation/firmware-guide/acpi/apei/einj.rst @@ -59,6 +59,9 @@ The following files belong to it: 0x00000200 Platform Correctable 0x00000400 Platform Uncorrectable non-fatal 0x00000800 Platform Uncorrectable fatal + V2_0x00000001 EINJV2 Processor Error + V2_0x00000002 EINJV2 Memory Error + V2_0x00000004 EINJV2 PCI Express Error ================ =================================== The format of the file contents are as above, except present are only @@ -88,6 +91,8 @@ The following files belong to it: Memory address and mask valid (param1 and param2). Bit 2 PCIe (seg,bus,dev,fn) valid (see param4 below). + Bit 3 + EINJv2 extension structure is valid If set to zero, legacy behavior is mimicked where the type of injection specifies just one bit set, and param1 is multiplexed. @@ -122,6 +127,13 @@ The following files belong to it: this actually works depends on what operations the BIOS actually includes in the trigger phase. +- component_id0 .. component_idN, component_syndrome0 .. component_syndromeN + + These files are used to set the "Component Array" field + of the EINJv2 Extension Structure. Each holds a 128-bit + hex value. Writing just a newline to any of these files + sets an invalid (all-ones) value. + CXL error types are supported from ACPI 6.5 onwards (given a CXL port is present). The EINJ user interface for CXL error types is at /cxl. The following files belong to it: @@ -194,6 +206,27 @@ An error injection example:: # echo 0x8 > error_type # Choose correctable memory error # echo 1 > error_inject # Inject now +An EINJv2 error injection example:: + + # cd /sys/kernel/debug/apei/einj + # cat available_error_type # See which errors can be injected + 0x00000002 Processor Uncorrectable non-fatal + 0x00000008 Memory Correctable + 0x00000010 Memory Uncorrectable non-fatal + V2_0x00000001 EINJV2 Processor Error + V2_0x00000002 EINJV2 Memory Error + + # echo 0x12345000 > param1 # Set memory address for injection + # echo 0xfffffffffffff000 > param2 # Range - anywhere in this page + # echo 0x1 > component_id0 # First device ID + # echo 0x4 > component_syndrome0 # First error syndrome + # echo 0x2 > component_id1 # Second device ID + # echo 0x4 > component_syndrome1 # Second error syndrome + # echo '' > component_id2 # Mark id2 invalid to terminate list + # echo V2_0x2 > error_type # Choose EINJv2 memory error + # echo 0xa > flags # set flags to indicate EINJv2 + # echo 1 > error_inject # Inject now + You should see something like this in dmesg:: [22715.830801] EDAC sbridge MC3: HANDLING MCE MEMORY ERROR -- GitLab From 9cf45756a4b9a7341333dfa8119b823ba66cd17e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 17 Jun 2025 16:18:24 -0700 Subject: [PATCH 164/868] ACPI: DPTF: Support for Wildcat Lake Add Wildcat Lake ACPI IDs for DPTF. Signed-off-by: Srinivas Pandruvada Link: https://patch.msgid.link/20250617231824.3314507-1-srinivas.pandruvada@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dptf/dptf_power.c | 2 ++ drivers/acpi/dptf/int340x_thermal.c | 7 +++++++ drivers/acpi/fan.h | 1 + drivers/thermal/intel/int340x_thermal/int3400_thermal.c | 1 + drivers/thermal/intel/int340x_thermal/int3403_thermal.c | 1 + 5 files changed, 12 insertions(+) diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c index e8caf4106ff96..776914f31b9ee 100644 --- a/drivers/acpi/dptf/dptf_power.c +++ b/drivers/acpi/dptf/dptf_power.c @@ -238,6 +238,8 @@ static const struct acpi_device_id int3407_device_ids[] = { {"INTC10A5", 0}, {"INTC10D8", 0}, {"INTC10D9", 0}, + {"INTC1100", 0}, + {"INTC1101", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, int3407_device_ids); diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c index aef7aca2161df..a222df059a16e 100644 --- a/drivers/acpi/dptf/int340x_thermal.c +++ b/drivers/acpi/dptf/int340x_thermal.c @@ -61,6 +61,13 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = { {"INTC10D7"}, {"INTC10D8"}, {"INTC10D9"}, + {"INTC10FC"}, + {"INTC10FD"}, + {"INTC10FE"}, + {"INTC10FF"}, + {"INTC1100"}, + {"INTC1101"}, + {"INTC1102"}, {""}, }; diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 15eba1c70e668..8a28a72a7c6a3 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -20,6 +20,7 @@ {"INTC106A", }, /* Fan for Lunar Lake generation */ \ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \ {"INTC10D6", }, /* Fan for Panther Lake generation */ \ + {"INTC10FE", }, /* Fan for Wildcat Lake generation */ \ {"PNP0C0B", } /* Generic ACPI fan */ #define ACPI_FPS_NAME_LEN 20 diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 0e07693ecf59e..ecb4ea443b9bb 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -690,6 +690,7 @@ static const struct acpi_device_id int3400_thermal_match[] = { {"INTC1068", 0}, {"INTC10A0", 0}, {"INTC10D4", 0}, + {"INTC10FC", 0}, {} }; diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c index 5a925a8df7b38..ba63796761ebc 100644 --- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c @@ -276,6 +276,7 @@ static const struct acpi_device_id int3403_device_ids[] = { {"INTC1069", 0}, {"INTC10A1", 0}, {"INTC10D5", 0}, + {"INTC10FD", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, int3403_device_ids); -- GitLab From 814eca1085ef26dae928372be374fd27690d24ab Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 8 Jun 2025 22:07:05 -0500 Subject: [PATCH 165/868] ACPI: Enable CONFIG_ACPI_DEBUG by default CONFIG_ACPI_DEBUG can be helpful for getting debug messages on OEM systems to identify a BIOS bug. It's a relatively small size increase to turn it on by default (50kb) and that saves asking people to enable it when an issue comes up because it wasn't in defconfig. Enable it by default. Signed-off-by: Mario Limonciello Link: https://patch.msgid.link/20250609030706.465202-1-superm1@kernel.org Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 7bc40c2735ac0..b594780a57d71 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -394,6 +394,7 @@ config ACPI_TABLE_OVERRIDE_VIA_BUILTIN_INITRD config ACPI_DEBUG bool "Debug Statements" + default y help The ACPI subsystem can produce debug output. Saying Y enables this output and increases the kernel size by around 50K. -- GitLab From b65b8ed7e80dd7e1c944a9fa75c47c030612f5a6 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 17 Jun 2025 16:19:40 -0700 Subject: [PATCH 166/868] thermal: int340x: processor_thermal: Add Wildcat Lake PCI ID Add Wildcat Lake PCI ID for processor thermal device. Signed-off-by: Srinivas Pandruvada Link: https://patch.msgid.link/20250617231940.3314546-1-srinivas.pandruvada@linux.intel.com Signed-off-by: Rafael J. Wysocki --- .../thermal/intel/int340x_thermal/processor_thermal_device.h | 1 + .../intel/int340x_thermal/processor_thermal_device_pci.c | 4 ++++ .../thermal/intel/int340x_thermal/processor_thermal_rfim.c | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h index 9a6ca43b6fa24..49398794124a2 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -31,6 +31,7 @@ #define PCI_DEVICE_ID_INTEL_SKL_THERMAL 0x1903 #define PCI_DEVICE_ID_INTEL_TGL_THERMAL 0x9A03 #define PCI_DEVICE_ID_INTEL_PTL_THERMAL 0xB01D +#define PCI_DEVICE_ID_INTEL_WCL_THERMAL 0xFD1D struct power_config { u32 index; diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c index 00160936070a8..d4d7e8e147d2b 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -499,6 +499,10 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, + { PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT | + PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | + PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT | + PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, { }, }; diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c index 3a028b78d9afc..1f3d22b659dbe 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c @@ -442,6 +442,7 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc switch (pdev->device) { case PCI_DEVICE_ID_INTEL_LNLM_THERMAL: case PCI_DEVICE_ID_INTEL_PTL_THERMAL: + case PCI_DEVICE_ID_INTEL_WCL_THERMAL: dlvr_mmio_regs_table = lnl_dlvr_mmio_regs; dlvr_mapping = lnl_dlvr_mapping; break; -- GitLab From deefc7083414de81aad102b60f0390f600d7eb79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Fri, 13 Jun 2025 12:14:12 +0200 Subject: [PATCH 167/868] gpio: mmio: add BGPIOF_NO_INPUT flag for GPO gpiochip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using bgpio_init with a gpiochip acting as a GPO (output only), the gpiochip ops `direction_input` was set to `bgpio_simple_dir_in` by default but we have no input ability. Adding this flag allows to set a valid ops for the `direction_output` ops without setting a valid ops for `direction_input` by default. Reviewed-by: Linus Walleij Signed-off-by: Clément Le Goffic Link: https://lore.kernel.org/r/20250613-hdp-upstream-v5-1-6fd6f0dc527c@foss.st.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 11 ++++++++++- include/linux/gpio/driver.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4841e4ebe7a67..09b9e1275e7ee 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -335,6 +335,11 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou return pinctrl_gpio_direction_input(gc, gpio); } +static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio) +{ + return -EINVAL; +} + static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) { return bgpio_dir_return(gc, gpio, false); @@ -566,7 +571,11 @@ static int bgpio_setup_direction(struct gpio_chip *gc, gc->direction_output = bgpio_dir_out_err; else gc->direction_output = bgpio_simple_dir_out; - gc->direction_input = bgpio_simple_dir_in; + + if (flags & BGPIOF_NO_INPUT) + gc->direction_input = bgpio_dir_in_err; + else + gc->direction_input = bgpio_simple_dir_in; } return 0; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index b53233051beed..97cc756232619 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -750,6 +750,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, #define BGPIOF_NO_OUTPUT BIT(5) /* only input */ #define BGPIOF_NO_SET_ON_INPUT BIT(6) #define BGPIOF_PINCTRL_BACKEND BIT(7) /* Call pinctrl direction setters */ +#define BGPIOF_NO_INPUT BIT(8) /* only output */ #ifdef CONFIG_GPIOLIB_IRQCHIP int gpiochip_irqchip_add_domain(struct gpio_chip *gc, -- GitLab From 1fd7d210952938e8ef6d87287e056e25a2fc0547 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Jun 2025 15:02:06 +0200 Subject: [PATCH 168/868] gpio: npcm-sgpio: don't use legacy GPIO chip setters We've converted this driver to using the new GPIO line value setters but missed the instance where the legacy callback is accessed directly using the function pointer. This will lead to a NULL-pointer dereference as this pointer is no longer populated. Fix it. Fixes: 0e1a8930c941 ("gpio: npcm-sgpio: use new GPIO line value setter callbacks") Link: https://lore.kernel.org/r/20250618-gpio-mmio-fix-setter-v1-1-2578ffb77019@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-npcm-sgpio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-npcm-sgpio.c b/drivers/gpio/gpio-npcm-sgpio.c index b3953d1ae8af4..25b203a89e380 100644 --- a/drivers/gpio/gpio-npcm-sgpio.c +++ b/drivers/gpio/gpio-npcm-sgpio.c @@ -211,9 +211,7 @@ static int npcm_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) static int npcm_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { - gc->set(gc, offset, val); - - return 0; + return gc->set_rv(gc, offset, val); } static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) -- GitLab From cbb887a76b788d8e9646fdd785f43745a3a662bb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Jun 2025 15:02:07 +0200 Subject: [PATCH 169/868] gpio: mmio: don't use legacy GPIO chip setters We've converted this driver to using the new GPIO line value setters but missed the instances where the legacy callback is accessed directly using the function pointer. This will lead to a NULL-pointer dereference as this pointer is no longer populated. The issue needs fixing locally as well as in the already converted previously users of gpio-mmio. Fixes: b908d35d0003 ("gpio: mmio: use new GPIO line value setter callbacks") Reported-by: Klara Modin Closes: https://lore.kernel.org/all/2rw2sncevdiyirpdovotztlg77apcq2btzytuv5jnm55aqhlne@swtts3hl53tw/ Tested-by: Klara Modin Tested-by: Marek Szyprowski Tested-by: Mark Brown Link: https://lore.kernel.org/r/20250618-gpio-mmio-fix-setter-v1-2-2578ffb77019@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 2 +- drivers/gpio/gpio-en7523.c | 2 +- drivers/gpio/gpio-mmio.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index c7ac5a9ffb1fd..3ba21add3a1c6 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -100,7 +100,7 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); if (priv->flags & MMIO_74XX_DIR_OUT) { - gc->set(gc, gpio, val); + gc->set_rv(gc, gpio, val); return 0; } diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index 69834db2c1cf2..c08069d0d1045 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -50,7 +50,7 @@ static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, iowrite32(dir, ctrl->dir[gpio / 16]); if (out) - gc->set(gc, gpio, val); + gc->set_rv(gc, gpio, val); iowrite32(output, ctrl->output); diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4d999f358cc49..08466e123818e 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -367,7 +367,7 @@ static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { - gc->set(gc, gpio, val); + gc->set_rv(gc, gpio, val); return bgpio_dir_return(gc, gpio, true); } @@ -432,14 +432,14 @@ static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, int val) { bgpio_dir_out(gc, gpio, val); - gc->set(gc, gpio, val); + gc->set_rv(gc, gpio, val); return bgpio_dir_return(gc, gpio, true); } static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, int val) { - gc->set(gc, gpio, val); + gc->set_rv(gc, gpio, val); bgpio_dir_out(gc, gpio, val); return bgpio_dir_return(gc, gpio, true); } -- GitLab From 9da895e97057ad946b2e727694af3fa5ee51d527 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 11:43:58 +0200 Subject: [PATCH 170/868] platform: cznic: use new GPIO line value setter callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Marek Behún Reviewed-by: Linus Walleij Acked-by: Marek Behún Link: https://lore.kernel.org/r/20250610-gpiochip-set-rv-platform-cznic-v1-1-30afd2444756@linaro.org Signed-off-by: Bartosz Golaszewski --- .../platform/cznic/turris-omnia-mcu-gpio.c | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/platform/cznic/turris-omnia-mcu-gpio.c b/drivers/platform/cznic/turris-omnia-mcu-gpio.c index c2df24ea86867..77184c8b42ea2 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c +++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c @@ -439,27 +439,28 @@ static int omnia_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, return 0; } -static void omnia_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int omnia_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { const struct omnia_gpio *gpio = &omnia_gpios[offset]; struct omnia_mcu *mcu = gpiochip_get_data(gc); u16 val, mask; if (!gpio->ctl_cmd) - return; + return -EINVAL; mask = BIT(gpio->ctl_bit); val = value ? mask : 0; - omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask); + return omnia_ctl_cmd(mcu, gpio->ctl_cmd, val, mask); } -static void omnia_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int omnia_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { unsigned long ctl = 0, ctl_mask = 0, ext_ctl = 0, ext_ctl_mask = 0; struct omnia_mcu *mcu = gpiochip_get_data(gc); unsigned int i; + int err; for_each_set_bit(i, mask, ARRAY_SIZE(omnia_gpios)) { unsigned long *field, *field_mask; @@ -488,13 +489,21 @@ static void omnia_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, guard(mutex)(&mcu->lock); - if (ctl_mask) - omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL, - ctl, ctl_mask); + if (ctl_mask) { + err = omnia_ctl_cmd_locked(mcu, OMNIA_CMD_GENERAL_CONTROL, + ctl, ctl_mask); + if (err) + return err; + } + + if (ext_ctl_mask) { + err = omnia_ctl_cmd_locked(mcu, OMNIA_CMD_EXT_CONTROL, + ext_ctl, ext_ctl_mask); + if (err) + return err; + } - if (ext_ctl_mask) - omnia_ctl_cmd_locked(mcu, OMNIA_CMD_EXT_CONTROL, - ext_ctl, ext_ctl_mask); + return 0; } static bool omnia_gpio_available(struct omnia_mcu *mcu, @@ -1015,8 +1024,8 @@ int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu) mcu->gc.direction_output = omnia_gpio_direction_output; mcu->gc.get = omnia_gpio_get; mcu->gc.get_multiple = omnia_gpio_get_multiple; - mcu->gc.set = omnia_gpio_set; - mcu->gc.set_multiple = omnia_gpio_set_multiple; + mcu->gc.set_rv = omnia_gpio_set; + mcu->gc.set_multiple_rv = omnia_gpio_set_multiple; mcu->gc.init_valid_mask = omnia_gpio_init_valid_mask; mcu->gc.can_sleep = true; mcu->gc.names = omnia_mcu_gpio_names; -- GitLab From cbf4e0fac347b78f1bcd29350b78184665ad487d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 11:42:19 +0300 Subject: [PATCH 171/868] ASoC: topology: Do not call snd_soc_remove_pcm_runtime() for ignored links If a link has been ignored then it is not even added. The snd_soc_get_pcm_runtime() will return NULL as the runtime will does not exist. We can just skip this step to avoid performing a lookup to do nothing. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619084222.559-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 7b0b8531bb32f..44b60324eaa2e 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -429,8 +429,11 @@ static void soc_tplg_remove_link(struct snd_soc_component *comp, dobj->unload(comp, dobj); list_del(&dobj->list); - snd_soc_remove_pcm_runtime(comp->card, - snd_soc_get_pcm_runtime(comp->card, link)); + + /* Ignored links do not need to be removed, they are not added */ + if (!link->ignore) + snd_soc_remove_pcm_runtime(comp->card, + snd_soc_get_pcm_runtime(comp->card, link)); } /* unload dai link */ -- GitLab From 2d91cb261cac6d885954b8f5da28b5c176c18131 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 11:42:20 +0300 Subject: [PATCH 172/868] ASoC: core: Check for rtd == NULL in snd_soc_remove_pcm_runtime() snd_soc_remove_pcm_runtime() might be called with rtd == NULL which will leads to null pointer dereference. This was reproduced with topology loading and marking a link as ignore due to missing hardware component on the system. On module removal the soc_tplg_remove_link() would call snd_soc_remove_pcm_runtime() with rtd == NULL since the link was ignored, no runtime was created. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619084222.559-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cfafdabcdc888..1ffabac1fb6b1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1139,6 +1139,9 @@ sanity_check: void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd) { + if (!rtd) + return; + lockdep_assert_held(&client_mutex); /* -- GitLab From 86591907527effbfd99c038ffc06ca30bb4f6b64 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 11:42:21 +0300 Subject: [PATCH 173/868] ASoC: Intel: skl_hda_dsp_generic: Implement add_dai_link to filter HDMI PCMs If the system does not have iDisp codec then mark the HDMI PCM link as ignore. This ensures that HDMI PCMs will not be created when there is no iDisp codec available. When iDisp codec is not present and the HDMI PCMs were created they were not operational, all operations would fail on them. With this patch it is possible to load the topology with HDMI links, but gives the ability to ignore them and thus prevent the creation of the nonworking PCM devices. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619084222.559-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_hda_dsp_generic.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 0554c7e2cb34e..519218385fdf7 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -85,6 +85,18 @@ skl_hda_get_board_quirk(struct snd_soc_acpi_mach_params *mach_params) return board_quirk; } +static int skl_hda_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); + + /* Ignore the HDMI PCM link if iDisp is not present */ + if (strstr(link->stream_name, "HDMI") && !ctx->hdmi.idisp_codec) + link->ignore = true; + + return 0; +} + static int skl_hda_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; @@ -101,6 +113,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->fully_routed = true; card->late_probe = skl_hda_card_late_probe; + card->add_dai_link = skl_hda_add_dai_link; dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk); -- GitLab From bb48117b79ebc39485f7306d09dc602981fe540f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 11:42:22 +0300 Subject: [PATCH 174/868] ASoC: Intel: sof_sdw: Implement add_dai_link to filter HDMI PCMs If the system does not have iDisp codec then mark the HDMI PCM link as ignore. This ensures that HDMI PCMs will not be created when there is no iDisp codec available. When iDisp codec is not present and the HDMI PCMs were created they were not operational, all operations would fail on them. With this patch it is possible to load the topology with HDMI links, but gives the ability to ignore them and thus prevent the creation of the nonworking PCM devices. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619084222.559-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 81a914bd7ec21..05d13bacf88a1 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1295,6 +1295,19 @@ static int sof_sdw_card_late_probe(struct snd_soc_card *card) return ret; } +static int sof_sdw_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private; + + /* Ignore the HDMI PCM link if iDisp is not present */ + if (strstr(link->stream_name, "HDMI") && !intel_ctx->hdmi.idisp_codec) + link->ignore = true; + + return 0; +} + static int mc_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); @@ -1321,6 +1334,7 @@ static int mc_probe(struct platform_device *pdev) card->name = "soundwire"; card->owner = THIS_MODULE; card->late_probe = sof_sdw_card_late_probe; + card->add_dai_link = sof_sdw_add_dai_link; snd_soc_card_set_drvdata(card, ctx); -- GitLab From cec49fa47bccadb288fd984ef2a5c45e8a2e2099 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:56:22 +0300 Subject: [PATCH 175/868] ASoC: SOF: ipc4-priv: Add kernel doc for fw_context_save of sof_ipc4_fw_data The kernel documentation is missing entry for the fw_context_save. Signed-off-by: Peter Ujfalusi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619105623.4546-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-priv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 58b0328206833..76dc54a2f07d9 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -72,6 +72,7 @@ struct sof_ipc4_fw_library { * @max_num_pipelines: max number of pipelines * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware + * @fw_context_save: Firmware supports full context save and restore * * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion -- GitLab From ace9b3daf2b4778358573d3698e34cb1c0fa7e14 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:56:23 +0300 Subject: [PATCH 176/868] ASoC: SOF: ipc4/Intel: Add support for library restore firmware functionality The firmware will be able to only save and restore the context related to library management. This means that even without a full context save, the libraries do not need to be re-loaded to the firmware after second or consecutive boots. This is reported via the FW_READY notification, where BIT(15) indicates: 0 - the library restore is not done 1 - library restore is done This bit is only valid if full context save is not enabled, full context save is by definition saves and restores the library related book-keeping as well. Add a new flag to tell the platform code if the libraries have been restored, no need to reload them after boot. Signed-off-by: Peter Ujfalusi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Kai Vehmanen Link: https://patch.msgid.link/20250619105623.4546-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 2 ++ sound/soc/sof/intel/hda-loader.c | 7 +++++-- sound/soc/sof/ipc4-loader.c | 6 ++++++ sound/soc/sof/ipc4-priv.h | 2 ++ sound/soc/sof/ipc4.c | 14 ++++++++++++-- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index f71d04736d176..e85c7afd85a4d 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -498,6 +498,8 @@ struct sof_ipc4_intel_mic_privacy_cap { #define SOF_IPC4_LOG_CORE_GET(x) (((x) & SOF_IPC4_LOG_CORE_MASK) >> \ SOF_IPC4_LOG_CORE_SHIFT) +#define SOF_IPC4_FW_READY_LIB_RESTORED BIT(15) + /* Value of notification type field - must fit into 8 bits */ enum sof_ipc4_notification_type { /* Phrase detected (notification from WoV module) */ diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 49085ca7b46bf..2cc11d8b0f708 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -579,8 +579,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, struct sof_ipc4_msg msg = {}; int ret, ret1; - /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */ - if (reload && hda->booted_from_imr && ipc4_data->fw_context_save) + /* + * if IMR booting is enabled and libraries have been restored during fw + * boot, skip the loading + */ + if (reload && hda->booted_from_imr && ipc4_data->libraries_restored) return 0; /* the fw_lib has been verified during loading, we can trust the validity here */ diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index d2f534d65edf1..ee61394e73d7c 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -494,6 +494,12 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) break; case SOF_IPC4_FW_CONTEXT_SAVE: ipc4_data->fw_context_save = *tuple->value; + /* + * Set the default libraries_restored value - if full + * context save is supported then it means that + * libraries are restored + */ + ipc4_data->libraries_restored = ipc4_data->fw_context_save; break; default: break; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 76dc54a2f07d9..45e9b78432f78 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -73,6 +73,7 @@ struct sof_ipc4_fw_library { * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware * @fw_context_save: Firmware supports full context save and restore + * @libraries_restored: The libraries have been retained during firmware boot * * @load_library: Callback function for platform dependent library loading * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion @@ -88,6 +89,7 @@ struct sof_ipc4_fw_data { int max_num_pipelines; u32 max_libs_count; bool fw_context_save; + bool libraries_restored; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 37e837b22ac88..0ba0e8e615ae7 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -576,9 +576,19 @@ EXPORT_SYMBOL(sof_ipc4_find_debug_slot_offset_by_type); static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg) { - /* no need to re-check version/ABI for subsequent boots */ - if (!sdev->first_boot) + if (!sdev->first_boot) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + + /* + * After the initial boot only check if the libraries have been + * restored when full context save is not enabled + */ + if (!ipc4_data->fw_context_save) + ipc4_data->libraries_restored = !!(ipc4_msg->primary & + SOF_IPC4_FW_READY_LIB_RESTORED); + return 0; + } sof_ipc4_create_exception_debugfs_node(sdev); -- GitLab From ecd41e0e2581d60f5268ad662065d17e0f049539 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 19 Jun 2025 15:11:19 +0300 Subject: [PATCH 177/868] ASoC: SOF: ipc4: Add sof_ipc4_pipeline_state_str() for debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sof_ipc4_pipeline_state_str() to translate enum sof_ipc4_pipeline_state into human readable form. Signed-off-by: Jyri Sarha Reviewed-by: Péter Ujfalusi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20250619121121.25241-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-priv.h | 3 +++ sound/soc/sof/ipc4.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 58b0328206833..a256528e030f2 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -123,4 +123,7 @@ size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev, void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state); +enum sof_ipc4_pipeline_state; +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state); + #endif diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 37e837b22ac88..0b91bd443025a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -237,6 +237,26 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms msg->extension, str); } } + +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) +{ + switch (state) { + case SOF_IPC4_PIPE_INVALID_STATE: + return " (INVALID_STATE)"; + case SOF_IPC4_PIPE_UNINITIALIZED: + return " (UNINITIALIZED)"; + case SOF_IPC4_PIPE_RESET: + return " (RESET)"; + case SOF_IPC4_PIPE_PAUSED: + return " (PAUSED)"; + case SOF_IPC4_PIPE_RUNNING: + return " (RUNNING)"; + case SOF_IPC4_PIPE_EOS: + return " (EOS)"; + default: + return " ()"; + } +} #else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg, bool data_size_valid) @@ -254,6 +274,11 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms else dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension); } + +const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) +{ + return ""; +} #endif static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, -- GitLab From 0e57fa20678d2d2dc0f047f052bb509c8f3ebc3b Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 19 Jun 2025 15:11:20 +0300 Subject: [PATCH 178/868] ASoC: SOF: ipc4-pcm: Pipe instances to dev_dbg in multi_pipeline_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a dev_dbg to sof_ipc4_set_multi_pipeline_state(). The debug print lists the pipeline instance numbers that are included in the SOF_IPC4_GLB_SET_PIPELINE_STATE message. Without this log its very hard to tell what pipelines are affected. This print is very helpful when analyzing SOF logs automatically. Signed-off-by: Jyri Sarha Reviewed-by: Péter Ujfalusi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20250619121121.25241-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 8eee3e1aadf93..be4518d8970bf 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -56,17 +56,41 @@ sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps) return stream_priv->time_info; } +static +char *sof_ipc4_set_multi_pipeline_state_debug(struct snd_sof_dev *sdev, char *buf, size_t size, + struct ipc4_pipeline_set_state_data *trigger_list) +{ + int i, offset = 0; + + for (i = 0; i < trigger_list->count; i++) { + offset += snprintf(buf + offset, size - offset, " %d", + trigger_list->pipeline_instance_ids[i]); + + if (offset >= size - 1) { + buf[size - 1] = '\0'; + break; + } + } + return buf; +} + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { struct sof_ipc4_msg msg = {{ 0 }}; u32 primary, ipc_size; + char debug_buf[32]; /* trigger a single pipeline */ if (trigger_list->count == 1) return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0], state); + dev_dbg(sdev->dev, "Set pipelines %s to state %d%s", + sof_ipc4_set_multi_pipeline_state_debug(sdev, debug_buf, sizeof(debug_buf), + trigger_list), + state, sof_ipc4_pipeline_state_str(state)); + primary = state; primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); -- GitLab From 2756b7f08ff6ca7c68c8c7dd61c8dc6895c9de34 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 19 Jun 2025 15:11:21 +0300 Subject: [PATCH 179/868] ASoC: SOF: ipc4-pcm: Harmonize sof_ipc4_set_pipeline_state() dbg print MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Harmonize sof_ipc4_set_pipeline_state() dbg print with the new print in sof_ipc4_set_multi_pipeline_state(). Signed-off-by: Jyri Sarha Reviewed-by: Péter Ujfalusi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20250619121121.25241-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index be4518d8970bf..cf809959eaea7 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -113,7 +113,8 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 s struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; - dev_dbg(sdev->dev, "ipc4 set pipeline instance %d state %d", instance_id, state); + dev_dbg(sdev->dev, "Set pipeline %d to state %d%s", instance_id, state, + sof_ipc4_pipeline_state_str(state)); primary = state; primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); -- GitLab From 8a07944a77e962b625c582696497964c393a3489 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:26:57 +0300 Subject: [PATCH 180/868] ASoC: SOF: ipc4-pcm: Look for best matching hw_config for SSP Instead of just looking for a hw_config with matching rate only it sounds better to try to find the best matching configuration. If we have multiple hw_configurations with the same rate, but each with different format for example then we have been picking the first config with the matching rate, which can be a problem and it wil depend on how the configs are ordered. Instead we should be trying to find the best match out of the configs 1. rate + format + channels are matching 2. rate + format are matching 3. rate matching Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250619102657.12109-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 61 ++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 8eee3e1aadf93..a5b365ab19454 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -557,12 +557,15 @@ static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0); } -static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, - struct snd_pcm_hw_params *params) +static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, + const char *link_name, + struct snd_pcm_hw_params *params) { struct snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; + int current_config = -1; + bool partial_match; int i; list_for_each_entry(slink, &sdev->dai_link_list, list) { @@ -573,19 +576,50 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const } if (!dai_link_found) - return; + return 0; + /* + * Find the first best matching hardware config: + * rate + format + channels are matching + * rate + channel are matching + * + * The copier cannot do rate and/or channel conversion. + */ for (i = 0; i < slink->num_hw_configs; i++) { struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i]; - if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) { - /* set current config for all DAI's with matching name */ - list_for_each_entry(dai, &sdev->dai_list, list) - if (!strcmp(slink->link->name, dai->name)) - dai->current_config = le32_to_cpu(hw_config->id); + if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate) && + params_width(params) == le32_to_cpu(hw_config->tdm_slot_width) && + params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) { + current_config = le32_to_cpu(hw_config->id); + partial_match = false; + /* best match found */ break; + } else if (current_config < 0 && + params_rate(params) == le32_to_cpu(hw_config->fsync_rate) && + params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) { + current_config = le32_to_cpu(hw_config->id); + partial_match = true; + /* keep looking for better match */ } } + + if (current_config < 0) { + dev_err(sdev->dev, + "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n", + __func__, slink->link->name, slink->num_hw_configs); + return -EINVAL; + } + + dev_dbg(sdev->dev, + "hw_config for %s: %d (num_hw_configs: %d) with %s match\n", + slink->link->name, current_config, slink->num_hw_configs, + partial_match ? "partial" : "full"); + list_for_each_entry(dai, &sdev->dai_list, list) + if (!strcmp(slink->link->name, dai->name)) + dai->current_config = current_config; + + return 0; } /* @@ -728,13 +762,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; } - switch (ipc4_copier->dai_type) { - case SOF_DAI_INTEL_SSP: - ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - break; - default: - break; - } + if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP) + return ipc4_ssp_dai_config_pcm_params_match(sdev, + (char *)rtd->dai_link->name, + params); return 0; } -- GitLab From 2710204bf1005ee5ca94d07fe740111a9677b919 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:46:08 +0300 Subject: [PATCH 181/868] ASoC: SOF: pcm: Remove local create_page_table() wrapper function The create_page_table() can be dropped and replaced with a direct call to snd_sof_create_page_table(). Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250619104608.25947-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d584a72e6f52f..acf1995e04e8f 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -20,24 +20,6 @@ #include "sof-utils.h" #include "ops.h" -/* Create DMA buffer page table for DSP */ -static int create_page_table(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - unsigned char *dma_area, size_t size) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_pcm *spcm; - struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); - int stream = substream->stream; - - spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) - return -EINVAL; - - return snd_sof_create_page_table(component->dev, dmab, - spcm->stream[stream].page_table.area, size); -} - /* * sof pcm period elapse work */ @@ -168,9 +150,11 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, /* create compressed page table for audio firmware */ if (runtime->buffer_changed) { - ret = create_page_table(component, substream, runtime->dma_area, - runtime->dma_bytes); + struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); + ret = snd_sof_create_page_table(component->dev, dmab, + spcm->stream[substream->stream].page_table.area, + runtime->dma_bytes); if (ret < 0) return ret; } -- GitLab From 6b3cb7f4341cbf62d41ccf6ea906dbe66be8aa3d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:26:40 +0300 Subject: [PATCH 182/868] ASoC: SOF: topology: Parse the dapm_widget_tokens in case of DSPless mode Parsing the dapm_widget_tokens is also needed for DSPless mode as it is setting the snd_soc_dapm_widget.no_wname_in_kcontrol_name flag for the kcontrol creation from DAPM widgets. Without that flag set, the following warnings might appear because of long control names: ALSA: Control name 'eqiir.2.1 Post Mixer Analog Playback IIR Eq bytes' truncated to 'eqiir.2.1 Post Mixer Analog Playback IIR Eq' ALSA: Control name 'eqfir.2.1 Post Mixer Analog Playback FIR Eq bytes' truncated to 'eqfir.2.1 Post Mixer Analog Playback FIR Eq' ALSA: Control name 'drc.2.1 Post Mixer Analog Playback DRC bytes' truncated to 'drc.2.1 Post Mixer Analog Playback DRC byte' ALSA: Control name 'drc.2.1 Post Mixer Analog Playback DRC switch' truncated to 'drc.2.1 Post Mixer Analog Playback DRC swit' ALSA: Control name 'gain.15.1 Pre Mixer Deepbuffer HDA Analog Volume' truncated to 'gain.15.1 Pre Mixer Deepbuffer HDA Analog V' Signed-off-by: Peter Ujfalusi Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250619102640.12068-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d612d693efc39..b6d5c8024f8cf 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2378,14 +2378,25 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw) { + struct snd_soc_tplg_private *priv = &tw->priv; + int ret; + + /* for snd_soc_dapm_widget.no_wname_in_kcontrol_name */ + ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, + ARRAY_SIZE(dapm_widget_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n", + w->name); + return ret; + } + if (WIDGET_IS_DAI(w->id)) { static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}}; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *priv = &tw->priv; struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; - int ret; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); if (!swidget) -- GitLab From 3d77763c9a6d94ebc258183583891fdf23599a18 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:45:51 +0300 Subject: [PATCH 183/868] ASoC: SOF: pcm: Reverse check for prepared stream in sof_pcm_hw_params() Reduce the number of checks needed with the simple and most common audio sequence when the stream is started then stopped. If the stream has not been prepared there is no need to check if we have pcm_ops and pcm_ops->hw_free() callback as it does not matter. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250619104551.25912-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index acf1995e04e8f..090ea3a76892a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -126,7 +126,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { + if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) { ret = pcm_ops->hw_free(component, substream); if (ret < 0) return ret; -- GitLab From f9c7c093797fc00c7ce3042387994cf9d6c6d4ce Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Thu, 19 Jun 2025 13:47:35 +0300 Subject: [PATCH 184/868] ASoC: SOF: ipc4-topology: Add load of ASRC component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds to IPC4 topology parsing the support for the asynchronous sample rate converter (ASRC) SOF component. It is applied for the DAPM widget type SND_SOC_TPLG_DAPM_ASRC. The parsed SOF tokens for ASRC are SOF_TKN_ASRC_RATE_OUT and SOF_TKN_ASRC_OPERATION_MODE. Signed-off-by: Seppo Ingalsuo Reviewed-by: Liam Girdwood Reviewed-by: Péter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20250619104735.26161-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 88 +++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc4-topology.h | 24 ++++++++++ 2 files changed, 112 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 540ba140e1559..089f2c9fe6936 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -145,6 +145,14 @@ static const struct sof_topology_token src_tokens[] = { offsetof(struct sof_ipc4_src_data, sink_rate)}, }; +/* ASRC */ +static const struct sof_topology_token asrc_tokens[] = { + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_asrc_data, out_freq)}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc4_asrc_data, asrc_mode)}, +}; + static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, @@ -166,6 +174,7 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)}, [SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)}, [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, + [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, }; struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, @@ -1045,6 +1054,50 @@ err: return ret; } +static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_pipeline *spipe = swidget->spipe; + struct sof_ipc4_asrc *asrc; + int ret; + + dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); + + asrc = kzalloc(sizeof(*asrc), GFP_KERNEL); + if (!asrc) + return -ENOMEM; + + swidget->private = asrc; + + ret = sof_ipc4_get_audio_fmt(scomp, swidget, &asrc->available_fmt, + &asrc->data.base_config); + if (ret) + goto err; + + ret = sof_update_ipc_object(scomp, &asrc->data, SOF_ASRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*asrc), 1); + if (ret) { + dev_err(scomp->dev, "Parsing ASRC tokens failed\n"); + goto err; + } + + spipe->core_mask |= BIT(swidget->core); + + dev_dbg(scomp->dev, "ASRC sink rate %d, mode 0x%08x\n", + asrc->data.out_freq, asrc->data.asrc_mode); + + ret = sof_ipc4_widget_setup_msg(swidget, &asrc->msg); + if (ret) + goto err; + + return 0; +err: + sof_ipc4_free_audio_fmt(&asrc->available_fmt); + kfree(asrc); + swidget->private = NULL; + return ret; +} + static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget) { struct sof_ipc4_src *src = swidget->private; @@ -1057,6 +1110,18 @@ static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget) swidget->private = NULL; } +static void sof_ipc4_widget_free_comp_asrc(struct snd_sof_widget *swidget) +{ + struct sof_ipc4_asrc *asrc = swidget->private; + + if (!asrc) + return; + + sof_ipc4_free_audio_fmt(&asrc->available_fmt); + kfree(swidget->private); + swidget->private = NULL; +} + static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget) { struct sof_ipc4_mixer *mixer = swidget->private; @@ -2817,6 +2882,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg = &src->msg; break; } + case snd_soc_dapm_asrc: + { + struct sof_ipc4_asrc *asrc = swidget->private; + + ipc_size = sizeof(asrc->data); + ipc_data = &asrc->data; + + msg = &asrc->msg; + break; + } case snd_soc_dapm_effect: { struct sof_ipc4_process *process = swidget->private; @@ -3503,6 +3578,15 @@ static enum sof_tokens src_token_list[] = { SOF_COMP_EXT_TOKENS, }; +static enum sof_tokens asrc_token_list[] = { + SOF_COMP_TOKENS, + SOF_ASRC_TOKENS, + SOF_AUDIO_FMT_NUM_TOKENS, + SOF_IN_AUDIO_FORMAT_TOKENS, + SOF_OUT_AUDIO_FORMAT_TOKENS, + SOF_COMP_EXT_TOKENS, +}; + static enum sof_tokens process_token_list[] = { SOF_COMP_TOKENS, SOF_AUDIO_FMT_NUM_TOKENS, @@ -3548,6 +3632,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY src_token_list, ARRAY_SIZE(src_token_list), NULL, sof_ipc4_prepare_src_module, NULL}, + [snd_soc_dapm_asrc] = {sof_ipc4_widget_setup_comp_asrc, sof_ipc4_widget_free_comp_asrc, + asrc_token_list, ARRAY_SIZE(asrc_token_list), + NULL, sof_ipc4_prepare_src_module, /* Common prepare with SRC */ + NULL}, [snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process, sof_ipc4_widget_free_comp_process, process_token_list, ARRAY_SIZE(process_token_list), diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index f4dc499c0ffe5..9e98cac19532b 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -435,6 +435,30 @@ struct sof_ipc4_src { struct sof_ipc4_msg msg; }; +/* + * struct sof_ipc4_asrc_data - IPC data for ASRC + * @base_config: IPC base config data + * @out_freq: Output rate for sink module, passed as such from topology to FW. + * @asrc_mode: Control for ASRC features with bit-fields, passed as such from topolgy to FW. + */ +struct sof_ipc4_asrc_data { + struct sof_ipc4_base_module_cfg base_config; + uint32_t out_freq; + uint32_t asrc_mode; +} __packed __aligned(4); + +/** + * struct sof_ipc4_asrc - ASRC config data + * @data: IPC base config data + * @available_fmt: Available audio format + * @msg: IPC4 message struct containing header and data info + */ +struct sof_ipc4_asrc { + struct sof_ipc4_asrc_data data; + struct sof_ipc4_available_audio_format available_fmt; + struct sof_ipc4_msg msg; +}; + /** * struct sof_ipc4_base_module_cfg_ext - base module config extension containing the pin format * information for the module. Both @num_input_pin_fmts and @num_output_pin_fmts cannot be 0 for a -- GitLab From ce4b269c26ac3216da44ef7699ee61791e6fcbc7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:25:48 +0300 Subject: [PATCH 185/868] ASoC: SOF: Intel: hda: Do not probe Soundwire in nocodec mode Soundwire is not needed for nocodec mode, skip probing it. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20250619102548.11928-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index bdfe388da198a..5347c433ac8eb 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -629,6 +629,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev) if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) goto skip_soundwire; + /* Skip SoundWire in nocodec mode */ + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + goto skip_soundwire; + /* scan SoundWire capabilities exposed by DSDT */ ret = hda_sdw_acpi_scan(sdev); if (ret < 0) { -- GitLab From a1d203d390e04798ccc1c3c06019cd4411885d6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 19 Jun 2025 13:28:48 +0300 Subject: [PATCH 186/868] ASoC: SOF: ipc4-pcm: Enable delay reporting for ChainDMA streams All streams (currently) which is configured to use ChainDMA can only work on Link/host DMA pairs where the link side position can be access via host registers (like HDA on CAVS 2.5 platforms). Since the firmware does not provide time_info for ChainDMA, unlike for HDA stream, the kernel should calculate the start and end offsets that is needed for the delay calculation. With this small change we can report accurate delays when the stream is configured to use ChainDMA. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Link: https://patch.msgid.link/20250619102848.12389-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 49 ++++++++++++++++++++++++++++++++--- sound/soc/sof/ipc4-topology.c | 6 ++--- sound/soc/sof/ipc4-topology.h | 1 + 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index a5b365ab19454..6e19d89e29632 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -409,9 +409,33 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * If use_chain_dma attribute is set we proceed to chained DMA * trigger function that handles the rest for the substream. */ - if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, - pipeline_list, state, cmd); + if (pipeline->use_chain_dma) { + struct sof_ipc4_timestamp_info *time_info; + + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); + + ret = sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, + pipeline_list, state, cmd); + if (ret || !time_info) + return ret; + + if (state == SOF_IPC4_PIPE_PAUSED) { + /* + * Record the DAI position for delay reporting + * To handle multiple pause/resume/xrun we need to add + * the positions to simulate how the firmware behaves + */ + u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component, + substream); + + time_info->stream_end_offset += pos; + } else if (state == SOF_IPC4_PIPE_RESET) { + /* Reset the end offset as the stream is stopped */ + time_info->stream_end_offset = 0; + } + + return 0; + } /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, @@ -959,8 +983,24 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, if (!host_copier || !dai_copier) return -EINVAL; - if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) + if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) { return -EINVAL; + } else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) { + /* + * While the firmware does not supports time_info reporting for + * streams using ChainDMA, it is granted that ChainDMA can only + * be used on Host+Link pairs where the link position is + * accessible from the host side. + * + * Enable delay calculation in case of ChainDMA via host + * accessible registers. + * + * The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts + * when 1ms data is available + */ + time_info->stream_start_offset = substream->runtime->rate / MSEC_PER_SEC; + goto out; + } node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id); offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg); @@ -978,6 +1018,7 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, time_info->stream_end_offset = ppl_reg.stream_end_offset; do_div(time_info->stream_end_offset, dai_sample_size); +out: /* * Calculate the wrap boundary need to be used for delay calculation * The host counter is in bytes, it will wrap earlier than the frames diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 089f2c9fe6936..591ee30551baa 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1938,10 +1938,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); /* - * Chain DMA does not support stream timestamping, set node_id to invalid - * to skip the code in sof_ipc4_get_stream_start_offset(). + * Chain DMA does not support stream timestamping, but it + * can use the host side registers for delay calculation. */ - copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; + copier_data->gtw_cfg.node_id = SOF_IPC4_CHAIN_DMA_NODE_ID; return 0; } diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 9e98cac19532b..14ba58d2be03f 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -58,6 +58,7 @@ #define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16 +#define SOF_IPC4_CHAIN_DMA_NODE_ID 0x7fffffff #define SOF_IPC4_INVALID_NODE_ID 0xffffffff /* FW requires minimum 2ms DMA buffer size */ -- GitLab From 5ed0d32805c19cfa5f03a25ec7e041dc845d3062 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:16 +0200 Subject: [PATCH 187/868] Documentation: gpio: undocument removed behavior Since commit 700cdf7ed00f ("gpio: sysfs: make the sysfs export behavior consistent"), named GPIO lines are no longer exported in sysfs as links named after the them. Drop the misleading bit from the ABI docs. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-1-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- Documentation/ABI/obsolete/sysfs-gpio | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index da1345d854b4a..8203bc2128db7 100644 --- a/Documentation/ABI/obsolete/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -19,7 +19,6 @@ Description: /export ... asks the kernel to export a GPIO to userspace /unexport ... to return a GPIO to the kernel /gpioN ... for each exported GPIO #N OR - / ... for a properly named GPIO line /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write: high, low /edge ... r/w as: none, falling, rising, both -- GitLab From 1ae86030745013d9d54fc287c1ce875f7ddd99e6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:17 +0200 Subject: [PATCH 188/868] Documentation: gpio: document the active_low field in the sysfs ABI Exported GPIO lines also have the active_low attribute which is not documented. Add a short mention for it. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-2-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- Documentation/ABI/obsolete/sysfs-gpio | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index 8203bc2128db7..480316fee6d80 100644 --- a/Documentation/ABI/obsolete/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -22,6 +22,7 @@ Description: /value ... always readable, writes fail for input GPIOs /direction ... r/w as: in, out (default low); write: high, low /edge ... r/w as: none, falling, rising, both + /active_low ... r/w as: 0, 1 /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N /label ... (r/o) descriptive, not necessarily unique -- GitLab From e1f02b40a741aac47016765c21b61e91d19aa1ec Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:18 +0200 Subject: [PATCH 189/868] gpio: sysfs: call mutex_destroy() in gpiod_unexport() While not critical, it's useful to have the corresponding call to mutex_destroy() whenever we use mutex_init(). Add the call right before kfreeing the GPIO data. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-3-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 4a3aa09dad9d5..cd3381a4bc93a 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -713,6 +713,7 @@ void gpiod_unexport(struct gpio_desc *desc) } put_device(dev); + mutex_destroy(&data->mutex); kfree(data); } EXPORT_SYMBOL_GPL(gpiod_unexport); -- GitLab From dc665b5248f90aa2dc74ecc1f2ebb731a6f5afd6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:19 +0200 Subject: [PATCH 190/868] gpio: sysfs: refactor the coding style Update the code to be more consistent with the rest of the codebase. Mostly correctly align line-breaks, remove unneeded tabs, stray newlines & spaces and tweak the comment style. No functional change. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-4-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 67 ++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index cd3381a4bc93a..88f97018fc799 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -73,7 +73,7 @@ static DEFINE_MUTEX(sysfs_lock); */ static ssize_t direction_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; @@ -88,11 +88,12 @@ static ssize_t direction_show(struct device *dev, } static ssize_t direction_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, const char *buf, + size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status; + ssize_t status; guard(mutex)(&data->mutex); @@ -109,12 +110,12 @@ static ssize_t direction_store(struct device *dev, } static DEVICE_ATTR_RW(direction); -static ssize_t value_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t value_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status; + ssize_t status; scoped_guard(mutex, &data->mutex) status = gpiod_get_value_cansleep(desc); @@ -125,8 +126,8 @@ static ssize_t value_show(struct device *dev, return sysfs_emit(buf, "%zd\n", status); } -static ssize_t value_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t value_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; @@ -179,22 +180,22 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) irq_flags = IRQF_SHARED; if (flags & GPIO_IRQF_TRIGGER_FALLING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; set_bit(FLAG_EDGE_FALLING, &desc->flags); } if (flags & GPIO_IRQF_TRIGGER_RISING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; set_bit(FLAG_EDGE_RISING, &desc->flags); } /* * FIXME: This should be done in the irq_request_resources callback - * when the irq is requested, but a few drivers currently fail - * to do so. + * when the irq is requested, but a few drivers currently fail to do + * so. * - * Remove this redundant call (along with the corresponding - * unlock) when those drivers have been fixed. + * Remove this redundant call (along with the corresponding unlock) + * when those drivers have been fixed. */ ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) @@ -240,15 +241,15 @@ static void gpio_sysfs_free_irq(struct device *dev) sysfs_put(data->value_kn); } -static const char * const trigger_names[] = { +static const char *const trigger_names[] = { [GPIO_IRQF_TRIGGER_NONE] = "none", [GPIO_IRQF_TRIGGER_FALLING] = "falling", [GPIO_IRQF_TRIGGER_RISING] = "rising", [GPIO_IRQF_TRIGGER_BOTH] = "both", }; -static ssize_t edge_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t edge_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); int flags; @@ -262,8 +263,8 @@ static ssize_t edge_show(struct device *dev, return sysfs_emit(buf, "%s\n", trigger_names[flags]); } -static ssize_t edge_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) +static ssize_t edge_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); ssize_t status = size; @@ -302,7 +303,6 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) struct gpio_desc *desc = data->desc; int status = 0; - if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; @@ -310,7 +310,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) /* reconfigure poll(2) support if enabled on one edge only */ if (flags == GPIO_IRQF_TRIGGER_FALLING || - flags == GPIO_IRQF_TRIGGER_RISING) { + flags == GPIO_IRQF_TRIGGER_RISING) { gpio_sysfs_free_irq(dev); status = gpio_sysfs_request_irq(dev, flags); } @@ -321,7 +321,7 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) } static ssize_t active_low_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; @@ -334,7 +334,8 @@ static ssize_t active_low_show(struct device *dev, } static ssize_t active_low_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) + struct device_attribute *attr, + const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); ssize_t status; @@ -397,8 +398,8 @@ static const struct attribute_group *gpio_groups[] = { * /ngpio ... matching gpio_chip.ngpio */ -static ssize_t base_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t base_show(struct device *dev, struct device_attribute *attr, + char *buf) { const struct gpio_device *gdev = dev_get_drvdata(dev); @@ -406,8 +407,8 @@ static ssize_t base_show(struct device *dev, } static DEVICE_ATTR_RO(base); -static ssize_t label_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t label_show(struct device *dev, struct device_attribute *attr, + char *buf) { const struct gpio_device *gdev = dev_get_drvdata(dev); @@ -415,8 +416,8 @@ static ssize_t label_show(struct device *dev, } static DEVICE_ATTR_RO(label); -static ssize_t ngpio_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, + char *buf) { const struct gpio_device *gdev = dev_get_drvdata(dev); @@ -439,8 +440,8 @@ ATTRIBUTE_GROUPS(gpiochip); * integer N ... number of GPIO to unexport */ static ssize_t export_store(const struct class *class, - const struct class_attribute *attr, - const char *buf, size_t len) + const struct class_attribute *attr, + const char *buf, size_t len) { struct gpio_desc *desc; int status, offset; @@ -498,8 +499,8 @@ done: static CLASS_ATTR_WO(export); static ssize_t unexport_store(const struct class *class, - const struct class_attribute *attr, - const char *buf, size_t len) + const struct class_attribute *attr, + const char *buf, size_t len) { struct gpio_desc *desc; int status; -- GitLab From 982ec96c3876349e65e60c7b4fd91d767099837e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:20 +0200 Subject: [PATCH 191/868] gpio: sysfs: remove unneeded headers No symbols from the linux/idr.h or linux/spinlock.h headers are used in this file so remove them. We also don't technically need linux/list.h currently but one of the follow-up commits will start using it so let's leave it. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-5-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 88f97018fc799..f23b4efea5905 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -12,7 +11,6 @@ #include #include #include -#include #include #include #include -- GitLab From fd19792851db77e74cff4e2dc772d25a83cdc34d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Jun 2025 16:38:21 +0200 Subject: [PATCH 192/868] gpio: sysfs: remove the mockdev pointer from struct gpio_device The usage of the mockdev pointer in struct gpio_device is limited to the GPIO sysfs code. There's no reason to keep it in this top-level structure. Create a separate structure containing the reference to the GPIO device and the dummy class device that will be passed to device_create_with_groups(). The !gdev->mockdev checks can be removed as long as we make sure that all operations on the GPIO class are protected with the sysfs lock. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250610-gpio-sysfs-chip-export-v1-6-a8c7aa4478b1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 81 +++++++++++++++++++++++------------- drivers/gpio/gpiolib.h | 3 -- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index f23b4efea5905..956411fc467a2 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -43,6 +43,11 @@ struct gpiod_data { bool direction_can_change; }; +struct gpiodev_data { + struct gpio_device *gdev; + struct device *cdev_base; /* Class device by GPIO base */ +}; + /* * Lock to serialise gpiod export and unexport, and prevent re-export of * gpiod whose chip is being unregistered. @@ -399,27 +404,27 @@ static const struct attribute_group *gpio_groups[] = { static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", gdev->base); + return sysfs_emit(buf, "%u\n", data->gdev->base); } static DEVICE_ATTR_RO(base); static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", gdev->label); + return sysfs_emit(buf, "%s\n", data->gdev->label); } static DEVICE_ATTR_RO(label); static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_device *gdev = dev_get_drvdata(dev); + const struct gpiodev_data *data = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", gdev->ngpio); + return sysfs_emit(buf, "%u\n", data->gdev->ngpio); } static DEVICE_ATTR_RO(ngpio); @@ -545,6 +550,26 @@ static const struct class gpio_class = { .class_groups = gpio_class_groups, }; +static int match_gdev(struct device *dev, const void *desc) +{ + struct gpiodev_data *data = dev_get_drvdata(dev); + const struct gpio_device *gdev = desc; + + return data && data->gdev == gdev; +} + +static struct gpiodev_data * +gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) +{ + struct device *cdev __free(put_device) = class_find_device(&gpio_class, + NULL, gdev, + match_gdev); + if (!cdev) + return NULL; + + return dev_get_drvdata(cdev); +}; + /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -590,12 +615,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) guard(mutex)(&sysfs_lock); - /* check if chip is being removed */ - if (!gdev->mockdev) { - status = -ENODEV; - goto err_clear_bit; - } - if (!test_bit(FLAG_REQUESTED, &desc->flags)) { gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__); status = -EPERM; @@ -719,9 +738,9 @@ EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_sysfs_register(struct gpio_device *gdev) { + struct gpiodev_data *data; struct gpio_chip *chip; struct device *parent; - struct device *dev; /* * Many systems add gpio chips for SOC support very early, @@ -747,32 +766,41 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) else parent = &gdev->dev; - /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev, - gpiochip_groups, GPIOCHIP_NAME "%d", - chip->base); - if (IS_ERR(dev)) - return PTR_ERR(dev); + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->gdev = gdev; guard(mutex)(&sysfs_lock); - gdev->mockdev = dev; + + /* use chip->base for the ID; it's already known to be unique */ + data->cdev_base = device_create_with_groups(&gpio_class, parent, + MKDEV(0, 0), data, + gpiochip_groups, + GPIOCHIP_NAME "%d", + chip->base); + if (IS_ERR(data->cdev_base)) { + kfree(data); + return PTR_ERR(data->cdev_base); + } return 0; } void gpiochip_sysfs_unregister(struct gpio_device *gdev) { + struct gpiodev_data *data; struct gpio_desc *desc; struct gpio_chip *chip; scoped_guard(mutex, &sysfs_lock) { - if (!gdev->mockdev) + data = gdev_get_data(gdev); + if (!data) return; - device_unregister(gdev->mockdev); - - /* prevent further gpiod exports */ - gdev->mockdev = NULL; + device_unregister(data->cdev_base); + kfree(data); } guard(srcu)(&gdev->srcu); @@ -798,9 +826,6 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) struct gpio_device *gdev = gc->gpiodev; int ret; - if (gdev->mockdev) - return 0; - ret = gpiochip_sysfs_register(gdev); if (ret) chip_err(gc, "failed to register the sysfs entry: %d\n", ret); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 58f64056de77b..9b74738a9ca5b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -27,8 +27,6 @@ * @dev: the GPIO device struct * @chrdev: character device for the GPIO device * @id: numerical ID number for the GPIO chip - * @mockdev: class device used by the deprecated sysfs interface (may be - * NULL) * @owner: helps prevent removal of modules exporting active GPIOs * @chip: pointer to the corresponding gpiochip, holding static * data for this device @@ -65,7 +63,6 @@ struct gpio_device { struct device dev; struct cdev chrdev; int id; - struct device *mockdev; struct module *owner; struct gpio_chip __rcu *chip; struct gpio_desc *descs; -- GitLab From bb0d147c9cf4a11cdc34321e276eb914648b0326 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Thu, 19 Jun 2025 15:17:45 +0800 Subject: [PATCH 193/868] ALSA: hda/realtek: Fixup ft alc257 rename alc3328 Audio ALC3328 recognized as ALC257, updated PCI ID 0x10EC12F0 to rename it to 3328. Signed-off-by: wangdicheng Link: https://patch.msgid.link/20250619071745.149299-1-wangdich9700@163.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 84a68c7d65b74..cfcc204264aa0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1088,6 +1088,7 @@ static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { { 0x10ec0668, 0x103c, 0, "ALC3662" }, { 0x10ec0283, 0x17aa, 0, "ALC3239" }, { 0x10ec0292, 0x17aa, 0, "ALC3232" }, + { 0x10ec0257, 0x12f0, 0, "ALC3328" }, { } /* terminator */ }; -- GitLab From bec7ac4700305020bfb2dd4d0bfc9e60dc908c5c Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 19 Jun 2025 19:40:48 +0200 Subject: [PATCH 194/868] ALSA: emu10k1: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250619174057.175676-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/pci/emu10k1/emu10k1.c | 3 ++- sound/pci/emu10k1/emu10k1x.c | 13 +++++++------ sound/pci/emu10k1/emufx.c | 23 ++++++++++++----------- sound/pci/emu10k1/emumixer.c | 9 +++++---- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index dadeda7758cee..548e7d0499013 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -154,7 +155,7 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, } else { struct snd_emu10k1_synth_arg *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Emu-10k1 Synth"); + strscpy(wave->name, "Emu-10k1 Synth"); arg->hwptr = emu; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 30ac37b5a2141..8c18ad9872236 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -840,15 +841,15 @@ static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) pcm->info_flags = 0; switch(device) { case 0: - strcpy(pcm->name, "EMU10K1X Front"); + strscpy(pcm->name, "EMU10K1X Front"); map = snd_pcm_std_chmaps; break; case 1: - strcpy(pcm->name, "EMU10K1X Rear"); + strscpy(pcm->name, "EMU10K1X Rear"); map = surround_map; break; case 2: - strcpy(pcm->name, "EMU10K1X Center/LFE"); + strscpy(pcm->name, "EMU10K1X Center/LFE"); map = clfe_map; break; } @@ -1461,7 +1462,7 @@ static int emu10k1x_midi_init(struct emu10k1x *emu, spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | @@ -1540,8 +1541,8 @@ static int __snd_emu10k1x_probe(struct pci_dev *pci, snd_emu10k1x_proc_init(chip); - strcpy(card->driver, "EMU10K1X"); - strcpy(card->shortname, "Dell Sound Blaster Live!"); + strscpy(card->driver, "EMU10K1X"); + strscpy(card->shortname, "Dell Sound Blaster Live!"); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->port, chip->irq); diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 03efc317e05f7..7db0660e6b61f 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1175,7 +1176,7 @@ snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; if (high_res_gpr_volume) { ctl->min = -1; @@ -1199,7 +1200,7 @@ snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; if (high_res_gpr_volume) { ctl->min = -1; @@ -1224,7 +1225,7 @@ snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->min = 0; @@ -1237,7 +1238,7 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl const char *name, int gpr, int defval) { ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, name); + strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; ctl->gpr[1] = gpr + 1; ctl->value[1] = defval; @@ -1325,7 +1326,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 256 / 8); - strcpy(icode->name, "Audigy DSP code for ALSA"); + strscpy(icode->name, "Audigy DSP code for ALSA"); ptr = 0; nctl = 0; gpr_map[bit_shifter16] = 0x00008000; @@ -1563,7 +1564,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[nctl + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1572,7 +1573,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[nctl + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -1849,7 +1850,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) /* clear TRAM data & address lines */ memset(icode->tram_valid, 0xff, 160 / 8); - strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); + strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela"); ptr = 0; i = 0; /* we have 12 inputs */ playback = SND_EMU10K1_INPUTS; @@ -2160,7 +2161,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) */ ctl = &controls[i + 0]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Bass"); + strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2170,7 +2171,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(ctl->id.name, "Tone Control - Treble"); + strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; ctl->min = 0; @@ -2623,7 +2624,7 @@ int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device) err = snd_hwdep_new(emu->card, "FX8010", device, &hw); if (err < 0) return err; - strcpy(hw->name, "EMU10K1 (FX8010)"); + strscpy(hw->name, "EMU10K1 (FX8010)"); hw->iface = SNDRV_HWDEP_IFACE_EMU10K1; hw->ops.open = snd_emu10k1_fx8010_open; hw->ops.ioctl = snd_emu10k1_fx8010_ioctl; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 05b98d9b547b2..d665d5d1ad7c6 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -1983,7 +1984,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -2188,11 +2189,11 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, } else { no_ac97: if (emu->card_capabilities->ecard) - strcpy(emu->card->mixername, "EMU APS"); + strscpy(emu->card->mixername, "EMU APS"); else if (emu->audigy) - strcpy(emu->card->mixername, "SB Audigy"); + strscpy(emu->card->mixername, "SB Audigy"); else - strcpy(emu->card->mixername, "Emu10k1"); + strscpy(emu->card->mixername, "Emu10k1"); } if (emu->audigy) -- GitLab From 962297a7cd9e2302c2a10291ff551ebe7f19213d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 19 Jun 2025 00:38:43 +0200 Subject: [PATCH 195/868] ALSA: aloop: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250618223844.1458-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 6c318a5903abe..a8902dc45dc11 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1368,7 +1369,7 @@ static int loopback_pcm_new(struct loopback *loopback, pcm->private_data = loopback; pcm->info_flags = 0; - strcpy(pcm->name, "Loopback PCM"); + strscpy(pcm->name, "Loopback PCM"); loopback->pcm[device] = pcm; return 0; @@ -1631,7 +1632,7 @@ static int loopback_mixer_new(struct loopback *loopback, int notify) struct loopback_setup *setup; int err, dev, substr, substr_count, idx; - strcpy(card->mixername, "Loopback Mixer"); + strscpy(card->mixername, "Loopback Mixer"); for (dev = 0; dev < 2; dev++) { pcm = loopback->pcm[dev]; substr_count = @@ -1824,8 +1825,8 @@ static int loopback_probe(struct platform_device *devptr) loopback_cable_proc_new(loopback, 0); loopback_cable_proc_new(loopback, 1); loopback_timer_source_proc_new(loopback); - strcpy(card->driver, "Loopback"); - strcpy(card->shortname, "Loopback"); + strscpy(card->driver, "Loopback"); + strscpy(card->shortname, "Loopback"); sprintf(card->longname, "Loopback %i", dev + 1); err = snd_card_register(card); if (err < 0) -- GitLab From 13ef21dffe7691a32c83a83d697d119c045536eb Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 16 Jun 2025 19:48:20 +0800 Subject: [PATCH 196/868] ASoC: SDCA: add support for HIDE entity properties and HID descriptor/report Add support for parsing the HIDE entity descriptor and HID descriptor/report Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250616114820.855401-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 29 ++++++++++++ sound/soc/sdca/sdca_functions.c | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index eaedb54a83227..856b0f40ce5eb 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -11,6 +11,7 @@ #include #include +#include struct device; struct sdca_entity; @@ -1040,6 +1041,32 @@ struct sdca_entity_ge { int num_modes; }; +/** + * struct sdca_entity_hide - information specific to HIDE Entities + * @hid: HID device structure + * @hidtx_ids: HIDTx Report ID + * @num_hidtx_ids: number of HIDTx Report ID + * @hidrx_ids: HIDRx Report ID + * @num_hidrx_ids: number of HIDRx Report ID + * @hide_reside_function_num: indicating which Audio Function Numbers within this Device + * @max_delay: the maximum time in microseconds allowed for the Device to change the ownership from Device to Host + * @af_number_list: which Audio Function Numbers within this Device are sending/receiving the messages in this HIDE + * @hid_desc: HID descriptor for the HIDE Entity + * @hid_report_desc: HID Report Descriptor for the HIDE Entity + */ +struct sdca_entity_hide { + struct hid_device *hid; + unsigned int *hidtx_ids; + int num_hidtx_ids; + unsigned int *hidrx_ids; + int num_hidrx_ids; + unsigned int hide_reside_function_num; + unsigned int max_delay; + unsigned int af_number_list[SDCA_MAX_FUNCTION_COUNT]; + struct hid_descriptor hid_desc; + unsigned char *hid_report_desc; +}; + /** * struct sdca_entity - information for one SDCA Entity * @label: String such as "OT 12". @@ -1055,6 +1082,7 @@ struct sdca_entity_ge { * @cs: Clock Source specific Entity properties. * @pde: Power Domain Entity specific Entity properties. * @ge: Group Entity specific Entity properties. + * @hide: HIDE Entity specific Entity properties. */ struct sdca_entity { const char *label; @@ -1071,6 +1099,7 @@ struct sdca_entity { struct sdca_entity_cs cs; struct sdca_entity_pde pde; struct sdca_entity_ge ge; + struct sdca_entity_hide hide; }; }; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 64ac264438907..4a89067dcf768 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1220,6 +1220,86 @@ bad_list: return -EINVAL; } +static int +find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, + struct fwnode_handle *entity_node, struct sdca_entity *entity) +{ + struct sdca_entity_hide *hide = &entity->hide; + unsigned int delay, *af_list = hide->af_number_list; + int nval, ret; + unsigned char *report_desc = NULL; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-RxUMP-ownership-transition-maxdelay", &delay); + if (!ret) + hide->max_delay = delay; + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids"); + if (nval > 0) { + hide->num_hidtx_ids = nval; + hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids, + sizeof(*hide->hidtx_ids), GFP_KERNEL); + if (!hide->hidtx_ids) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDTx-supported-report-ids", + hide->hidtx_ids, + hide->num_hidtx_ids); + if (ret < 0) + return ret; + } + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids"); + if (nval > 0) { + hide->num_hidrx_ids = nval; + hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids, + sizeof(*hide->hidrx_ids), GFP_KERNEL); + if (!hide->hidrx_ids) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(entity_node, + "mipi-sdca-HIDRx-supported-report-ids", + hide->hidrx_ids, + hide->num_hidrx_ids); + if (ret < 0) + return ret; + } + + nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list"); + if (nval <= 0) { + dev_err(dev, "%pfwP: audio function numbers list missing: %d\n", + entity_node, nval); + return -EINVAL; + } else if (nval > SDCA_MAX_FUNCTION_COUNT) { + dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node); + return -EINVAL; + } + + hide->hide_reside_function_num = nval; + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-hide-related-audio-function-list", af_list, nval); + + nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor"); + if (nval) + fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor", + (u8 *)&hide->hid_desc, nval); + + if (hide->hid_desc.bNumDescriptors) { + nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor"); + if (nval) { + report_desc = devm_kzalloc(dev, nval, GFP_KERNEL); + if (!report_desc) + return -ENOMEM; + hide->hid_report_desc = report_desc; + fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", + report_desc, nval); + } + } + + return 0; +} + static int find_sdca_entity(struct device *dev, struct fwnode_handle *function_node, struct fwnode_handle *entity_node, @@ -1261,6 +1341,9 @@ static int find_sdca_entity(struct device *dev, case SDCA_ENTITY_TYPE_GE: ret = find_sdca_entity_ge(dev, entity_node, entity); break; + case SDCA_ENTITY_TYPE_HIDE: + ret = find_sdca_entity_hide(dev, function_node, entity_node, entity); + break; default: break; } -- GitLab From 3421d46440ebe0865bec71dbd2330b4e17a425ab Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 16 Jun 2025 19:49:07 +0800 Subject: [PATCH 197/868] HID: core: Add bus define for SoundWire bus SDCA (SoundWire Device Class for Audio) uses HID to convey input events from peripheral devices. Add a bus define for the SoundWire bus to prepare support for this. Signed-off-by: Charles Keepax Signed-off-by: Shuming Fan Acked-by: Jiri Kosina Link: https://patch.msgid.link/20250616114907.855452-1-shumingf@realtek.com Signed-off-by: Mark Brown --- drivers/hid/hid-core.c | 3 +++ include/uapi/linux/input.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b348d0464314c..b419e31005b87 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2294,6 +2294,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_I2C: bus = "I2C"; break; + case BUS_SDW: + bus = "SOUNDWIRE"; + break; case BUS_VIRTUAL: bus = "VIRTUAL"; break; diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 2557eb7b05617..127119c287cff 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -275,6 +275,7 @@ struct input_mask { #define BUS_CEC 0x1E #define BUS_INTEL_ISHTP 0x1F #define BUS_AMD_SFH 0x20 +#define BUS_SDW 0x21 /* * MT_TOOL types -- GitLab From 87aafc8580acf87fcaf1a7e30ed858d8c8d37d81 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sat, 21 Jun 2025 11:52:24 -0700 Subject: [PATCH 198/868] ALSA: intel8x0: Fix incorrect codec index usage in mixer for ICH4 code mistakenly used a hardcoded index (codec[1]) instead of iterating, over the codec array using the loop variable i. Use codec[i] instead of codec[1] to match the loop iteration. Signed-off-by: Alok Tiwari Link: https://patch.msgid.link/20250621185233.4081094-1-alok.a.tiwari@oracle.com Signed-off-by: Takashi Iwai --- sound/pci/intel8x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 51e7f1f1a48e4..b521cec203336 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2249,7 +2249,7 @@ static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT; for (i = 1; i < 4; i++) { if (pcm->r[0].codec[i]) { - tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT; + tmp |= chip->ac97_sdin[pcm->r[0].codec[i]->num] << ICH_DI2L_SHIFT; break; } } -- GitLab From 2f6ff1e615cd4f8a86c9d0e4e778db9ae44d5481 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Jun 2025 13:54:08 +0200 Subject: [PATCH 199/868] ASoC: codecs: wcd937x: Use simple defines for chipid register value The value used to identify chip variant is not an enumeration, but raw value used to compare registers with. The 'enum' is not used in the code at all, so simplify and make it a raw hex value define, so intention will be explicit. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250616-asoc-wcd93xx-enum-v1-1-a20a1b538509@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd937x.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index a5cf6015122cc..3b0a8cc314e05 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -24,10 +24,8 @@ #include "wcd-mbhc-v2.h" #include "wcd937x.h" -enum { - CHIPID_WCD9370 = 0, - CHIPID_WCD9375 = 5, -}; +#define CHIPID_WCD9370 0x0 +#define CHIPID_WCD9375 0x5 /* Z value defined in milliohm */ #define WCD937X_ZDET_VAL_32 (32000) -- GitLab From 100877df34b00257c9ed6d279b06b3ed1da5e2c1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Jun 2025 13:54:09 +0200 Subject: [PATCH 200/868] ASoC: codecs: wcd938x: Use simple defines for chipid register value The value used to identify chip variant is not an enumeration, but raw value used to compare registers with. The 'enum' is not used in the code at all, so simplify and make it a raw hex value define, so intention will be explicit. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250616-asoc-wcd93xx-enum-v1-2-a20a1b538509@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd938x.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 6401ac32d1b6f..711f373ece24c 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -25,6 +25,9 @@ #include "wcd-mbhc-v2.h" #include "wcd938x.h" +#define CHIPID_WCD9380 0x0 +#define CHIPID_WCD9385 0x5 + #define WCD938X_MAX_MICBIAS (4) #define WCD938X_MBHC_MAX_BUTTONS (8) #define TX_ADC_MAX (4) @@ -72,11 +75,6 @@ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, snd_soc_get_volsw, \ wcd938x_ear_pa_put_gain, tlv_array) -enum { - WCD9380 = 0, - WCD9385 = 5, -}; - enum { /* INTR_CTRL_INT_MASK_0 */ WCD938X_IRQ_MBHC_BUTTON_PRESS_DET = 0, @@ -3119,7 +3117,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd938x->aux_pdm_wd_int); switch (variant) { - case WCD9380: + case CHIPID_WCD9380: ret = snd_soc_add_component_controls(component, wcd9380_snd_controls, ARRAY_SIZE(wcd9380_snd_controls)); if (ret < 0) { @@ -3129,7 +3127,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component) goto err_free_aux_pdm_wd_int; } break; - case WCD9385: + case CHIPID_WCD9385: ret = snd_soc_add_component_controls(component, wcd9385_snd_controls, ARRAY_SIZE(wcd9385_snd_controls)); if (ret < 0) { -- GitLab From 5d3ccd356e2ce510bdbd4b0c389f2fe3deb41a45 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Jun 2025 13:54:10 +0200 Subject: [PATCH 201/868] ASoC: codecs: wcd939x: Use simple defines for chipid register value The value used to identify chip variant is not an enumeration, but raw value used to compare registers with. The 'enum' is not used in the code at all, so simplify and make it a raw hex value define, so intention will be explicit. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250616-asoc-wcd93xx-enum-v1-3-a20a1b538509@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 45645abe30856..7ec751a6cd261 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -36,6 +36,9 @@ #define TX_ADC_MAX (4) #define WCD_MBHC_HS_V_MAX 1600 +#define CHIPID_WCD9390 0x0 +#define CHIPID_WCD9395 0x5 + enum { WCD939X_VERSION_1_0 = 0, WCD939X_VERSION_1_1, @@ -85,11 +88,6 @@ enum { /* Z value compared in milliOhm */ #define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024) -enum { - WCD9390 = 0, - WCD9395 = 5, -}; - enum { /* INTR_CTRL_INT_MASK_0 */ WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0, @@ -1483,7 +1481,7 @@ static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, if (mode_val == wcd939x->hph_mode) return 0; - if (wcd939x->variant == WCD9390) { + if (wcd939x->variant == CHIPID_WCD9390) { switch (mode_val) { case CLS_H_NORMAL: case CLS_H_LP: @@ -3065,7 +3063,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd939x->ear_pdm_wd_int); switch (wcd939x->variant) { - case WCD9390: + case CHIPID_WCD9390: ret = snd_soc_add_component_controls(component, wcd9390_snd_controls, ARRAY_SIZE(wcd9390_snd_controls)); if (ret < 0) { @@ -3075,7 +3073,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component) goto err_free_ear_pdm_wd_int; } break; - case WCD9395: + case CHIPID_WCD9395: ret = snd_soc_add_component_controls(component, wcd9395_snd_controls, ARRAY_SIZE(wcd9395_snd_controls)); if (ret < 0) { -- GitLab From a48352921f0b15b1f7eff83f5b5613d6ae2350d3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 16 Jun 2025 13:54:11 +0200 Subject: [PATCH 202/868] ASoC: codecs: wcd939x: Add defines for major/minor version decoding Replace hard-coded register values with defines for checking major and minor versions of device. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250616-asoc-wcd93xx-enum-v1-4-a20a1b538509@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 7ec751a6cd261..64f082e474c1d 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -39,6 +39,11 @@ #define CHIPID_WCD9390 0x0 #define CHIPID_WCD9395 0x5 +/* Version major: 1.x */ +#define CHIPID_WCD939X_VER_MAJOR_1 0x0 +/* Version minor: x.1 */ +#define CHIPID_WCD939X_VER_MINOR_1 0x3 + enum { WCD939X_VERSION_1_0 = 0, WCD939X_VERSION_1_1, @@ -3449,8 +3454,8 @@ static int wcd939x_bind(struct device *dev) regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1); regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1); - if (id1 == 0) - version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); + if (id1 == CHIPID_WCD939X_VER_MAJOR_1) + version = ((status1 & CHIPID_WCD939X_VER_MINOR_1) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0); else version = WCD939X_VERSION_2_0; -- GitLab From ac558015dfd803626622bd0ba9645d58a3ed16b1 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 16 Jun 2025 19:49:29 +0800 Subject: [PATCH 203/868] ASoC: SDCA: add a HID device for HIDE entity This patch supports to add a HID device for SDCA HIDE entity. The codec driver could call 'hid_input_report' to report events. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250616114929.855496-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_hid.h | 25 +++++++ sound/soc/sdca/Kconfig | 4 + sound/soc/sdca/Makefile | 3 + sound/soc/sdca/sdca_functions.c | 9 +++ sound/soc/sdca/sdca_hid.c | 127 ++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 include/sound/sdca_hid.h create mode 100644 sound/soc/sdca/sdca_hid.c diff --git a/include/sound/sdca_hid.h b/include/sound/sdca_hid.h new file mode 100644 index 0000000000000..8ab3e498884ef --- /dev/null +++ b/include/sound/sdca_hid.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + */ + +#ifndef __SDCA_HID_H__ +#define __SDCA_HID_H__ + +#include +#include + +#if IS_ENABLED(CONFIG_SND_SOC_SDCA_HID) +int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity); + +#else +static inline int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) +{ + return 0; +} + +#endif + +#endif /* __SDCA_HID_H__ */ diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index ee20b9914aa1f..ec28855fe3b09 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -9,3 +9,7 @@ config SND_SOC_SDCA config SND_SOC_SDCA_OPTIONAL def_tristate SND_SOC_SDCA || !SND_SOC_SDCA + +config SND_SOC_SDCA_HID + tristate "SDCA HID support" + depends on SND_SOC_SDCA && HID diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 53344f108ca67..9af46e7edfd2a 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -2,4 +2,7 @@ snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o +snd-soc-sdca-hid-y := sdca_hid.o + +obj-$(CONFIG_SND_SOC_SDCA_HID) += snd-soc-sdca-hid.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 4a89067dcf768..093c681e93879 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -19,6 +19,7 @@ #include #include #include +#include /* * Should be long enough to encompass all the MIPI DisCo properties. @@ -1294,6 +1295,13 @@ find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node, hide->hid_report_desc = report_desc; fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor", report_desc, nval); + + /* add HID device */ + ret = sdca_add_hid_device(dev, entity); + if (ret) { + dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret); + return ret; + } } } @@ -1933,3 +1941,4 @@ EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); +MODULE_IMPORT_NS("SND_SOC_SDCA_HID"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c new file mode 100644 index 0000000000000..b227ad94d08ff --- /dev/null +++ b/sound/soc/sdca/sdca_hid.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int sdwhid_parse(struct hid_device *hid) +{ + struct sdca_entity *entity = hid->driver_data; + unsigned int rsize; + int ret; + + rsize = entity->hide.hid_desc.rpt_desc.wDescriptorLength; + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); + return -EINVAL; + } + + ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize); + + if (!ret) + return 0; + + dev_err(&hid->dev, "parsing report descriptor failed\n"); + return ret; +} + +static int sdwhid_start(struct hid_device *hid) +{ + return 0; +} + +static void sdwhid_stop(struct hid_device *hid) +{ +} + +static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + /* not implemented yet */ + return 0; + case HID_REQ_SET_REPORT: + /* not implemented yet */ + return 0; + default: + return -EIO; + } +} + +static int sdwhid_open(struct hid_device *hid) +{ + return 0; +} + +static void sdwhid_close(struct hid_device *hid) +{ +} + +static const struct hid_ll_driver sdw_hid_driver = { + .parse = sdwhid_parse, + .start = sdwhid_start, + .stop = sdwhid_stop, + .open = sdwhid_open, + .close = sdwhid_close, + .raw_request = sdwhid_raw_request, +}; + +int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) +{ + struct sdw_bus *bus; + struct hid_device *hid; + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret; + + bus = slave->bus; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &sdw_hid_driver; + + hid->dev.parent = dev; + hid->bus = BUS_SDW; + hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID); + + snprintf(hid->name, sizeof(hid->name), + "HID sdw:%01x:%01x:%04x:%04x:%02x", + bus->controller_id, bus->link_id, slave->id.mfg_id, + slave->id.part_id, slave->id.class_id); + + snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name); + + hid->driver_data = entity; + + ret = hid_add_device(hid); + if (ret && ret != -ENODEV) { + dev_err(dev, "can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + entity->hide.hid = hid; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA_HID"); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SDCA HID library"); -- GitLab From c4ca928a6db1593802cd945f075a7e21dd0430c1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 13 Jun 2025 17:41:04 +0100 Subject: [PATCH 204/868] ASoC: hdac_hdmi: Rate limit logging on connection and disconnection We currently log parse failures for ELD data and some disconnection events as errors without rate limiting. These log messages can be triggered very frequently in some situations, especially ELD parsing when there is nothing connected to a HDMI port which will generate: hdmi-audio-codec hdmi-audio-codec.1.auto: HDMI: Unknown ELD version 0 While there's doubtless work that could be done on reducing the number of connection notification callbacks it's possible these may be legitimately generated by poor quality physical connections so let's use rate limiting to mitigate the log spam for the parse errors and lower the severity for disconnect logging to debug level. Signed-off-by: Mark Brown Link: https://patch.msgid.link/20250613-asoc-hdmi-eld-logging-v1-1-76d64154d969@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e05f0bf91fa52..3aae0a2eb047a 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1231,7 +1231,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev, >> DRM_ELD_VER_SHIFT; if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { - dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); + dev_err_ratelimited(&hdev->dev, + "HDMI: Unknown ELD version %d\n", ver); return -EINVAL; } @@ -1239,7 +1240,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev, DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; if (mnl > ELD_MAX_MNL) { - dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); + dev_err_ratelimited(&hdev->dev, + "HDMI: MNL Invalid %d\n", mnl); return -EINVAL; } @@ -1298,8 +1300,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, if (!port->eld.monitor_present || !port->eld.eld_valid) { - dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", - __func__, pin->nid, port->id); + dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); /* * PCMs are not registered during device probe, so don't -- GitLab From 19cbc930c209d59a2c9828de4c7b767e9f14667e Mon Sep 17 00:00:00 2001 From: Primoz Fiser Date: Wed, 18 Jun 2025 08:33:39 +0200 Subject: [PATCH 205/868] regulator: pca9450: Support PWM mode also for pca9451a Previous commit 548d770c330c ("regulator: pca9450: Add support for mode operations") added support for setting forced PWM mode on the buck regulators for pca9450a and pca9450bc parts. However part pca9451a also supports this feature, thus add support for it. Fixes: 548d770c330c ("regulator: pca9450: Add support for mode operations") Signed-off-by: Primoz Fiser Link: https://patch.msgid.link/20250618063339.2508893-1-primoz.fiser@norik.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index f6faf14a9c536..feadb21a8f307 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -873,12 +873,15 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -902,12 +905,15 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -927,6 +933,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -946,6 +957,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -965,6 +981,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { -- GitLab From 545daf90910ec83e167cf3fbcc31fcf5467432b8 Mon Sep 17 00:00:00 2001 From: Naoya Tezuka Date: Fri, 20 Jun 2025 15:28:22 +0900 Subject: [PATCH 206/868] platform/chrome: chromeos_pstore: Add ecc_size module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On ChromiumOS devices, the `ecc_size` is set to 0 (check dmesg | grep ecc  to see `ecc: 0`): this disables ECC for ramoops region, even when  `ramoops.ecc=1` is given to kernel command line parameter. Introduce `ecc_size` module parameter to provide a method to turn on ECC for ramoops and set different values of ecc_size per devices. A large `ecc_size` value can cause a kernel panic due to a constraint in Reed-Solomon code library. The validation for this constraint should belong to the common pstore RAM layer, not in each individual driver. So this check is handled by a separate patch [1]. [1] https://lore.kernel.org/lkml/20250620054757.1006729-1-naoyatezuka@chromium.org Signed-off-by: Naoya Tezuka Link: https://lore.kernel.org/r/20250620062822.1018798-1-naoyatezuka@chromium.org Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/chromeos_pstore.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c index f37c0ef4af1fd..a6eed99507d47 100644 --- a/drivers/platform/chrome/chromeos_pstore.c +++ b/drivers/platform/chrome/chromeos_pstore.c @@ -9,6 +9,10 @@ #include #include +static int ecc_size; +module_param(ecc_size, int, 0400); +MODULE_PARM_DESC(ecc_size, "ECC parity data size in bytes. A positive value enables ECC for the ramoops region."); + static const struct dmi_system_id chromeos_pstore_dmi_table[] __initconst = { { /* @@ -117,6 +121,9 @@ static int __init chromeos_pstore_init(void) { bool acpi_dev_found; + if (ecc_size > 0) + chromeos_ramoops_data.ecc_info.ecc_size = ecc_size; + /* First check ACPI for non-hardcoded values from firmware. */ acpi_dev_found = chromeos_check_acpi(); -- GitLab From e6bb78570f7d531622ec572ef9ddbe6e66ff16ce Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 23 Jun 2025 00:02:21 +0200 Subject: [PATCH 207/868] gpio: sysfs: fix use-after-free in error path When invoking device_create_with_groups(), its return value is stored in `data->cdev_base`. However, in case of faiure, `data` is first freed and then derefernced in order to return `data->cdev_base`. Fix the use-after-free by extracting the error code before free'ing `data`. Fixes: fd19792851db ("gpio: sysfs: remove the mockdev pointer from struct gpio_device") Addresses-Coverity-ID: 1644512 ("Memory - illegal accesses (USE_AFTER_FREE)") Signed-off-by: Antonio Quartulli Link: https://lore.kernel.org/r/20250622220221.28025-1-antonio@mandelbit.com [Bartosz: added Fixes: tag, tweaked commit message] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 956411fc467a2..c4c21e25c682b 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -741,6 +741,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) struct gpiodev_data *data; struct gpio_chip *chip; struct device *parent; + int err; /* * Many systems add gpio chips for SOC support very early, @@ -781,8 +782,9 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) GPIOCHIP_NAME "%d", chip->base); if (IS_ERR(data->cdev_base)) { + err = PTR_ERR(data->cdev_base); kfree(data); - return PTR_ERR(data->cdev_base); + return err; } return 0; -- GitLab From a0cfbc67d71c763357a8847305e3d5254028c0da Mon Sep 17 00:00:00 2001 From: Daniel Sullivan Date: Sat, 21 Jun 2025 17:12:33 -0700 Subject: [PATCH 208/868] gpio: ts5500: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Daniel Sullivan Link: https://lore.kernel.org/r/aFdKce3Go9iF4A6m@danv-Standard-PC-Q35-ICH9-2009 Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ts5500.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c index 61cbec5c06a75..bb432ed73698b 100644 --- a/drivers/gpio/gpio-ts5500.c +++ b/drivers/gpio/gpio-ts5500.c @@ -244,7 +244,7 @@ static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val) return 0; } -static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { struct ts5500_priv *priv = gpiochip_get_data(chip); const struct ts5500_dio line = priv->pinout[offset]; @@ -256,6 +256,8 @@ static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) else ts5500_clear_mask(line.value_mask, line.value_addr); spin_unlock_irqrestore(&priv->lock, flags); + + return 0; } static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -338,7 +340,7 @@ static int ts5500_dio_probe(struct platform_device *pdev) priv->gpio_chip.direction_input = ts5500_gpio_input; priv->gpio_chip.direction_output = ts5500_gpio_output; priv->gpio_chip.get = ts5500_gpio_get; - priv->gpio_chip.set = ts5500_gpio_set; + priv->gpio_chip.set_rv = ts5500_gpio_set; priv->gpio_chip.to_irq = ts5500_gpio_to_irq; priv->gpio_chip.base = -1; -- GitLab From 40b71f1171da13664e9677fbd993d9098315d71e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:14 +0200 Subject: [PATCH 209/868] gpio: pca9570: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-1-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca9570.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index d37ba40493683..a33246f20fd83 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -88,7 +88,7 @@ static int pca9570_get(struct gpio_chip *chip, unsigned offset) return !!(buffer & BIT(offset)); } -static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) +static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value) { struct pca9570 *gpio = gpiochip_get_data(chip); u8 buffer; @@ -110,6 +110,7 @@ static void pca9570_set(struct gpio_chip *chip, unsigned offset, int value) out: mutex_unlock(&gpio->lock); + return ret; } static int pca9570_probe(struct i2c_client *client) @@ -125,7 +126,7 @@ static int pca9570_probe(struct i2c_client *client) gpio->chip.owner = THIS_MODULE; gpio->chip.get_direction = pca9570_get_direction; gpio->chip.get = pca9570_get; - gpio->chip.set = pca9570_set; + gpio->chip.set_rv = pca9570_set; gpio->chip.base = -1; gpio->chip_data = device_get_match_data(&client->dev); gpio->chip.ngpio = gpio->chip_data->ngpio; -- GitLab From 4027438be823abdcfb50ee2abed06fa4e6af4b7c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:15 +0200 Subject: [PATCH 210/868] gpio: pcf857x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-2-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pcf857x.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 2e5f5d7f88659..a042036803336 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -171,21 +171,24 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value return status; } -static void pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value) +static int pcf857x_set(struct gpio_chip *chip, unsigned int offset, int value) { - pcf857x_output(chip, offset, value); + return pcf857x_output(chip, offset, value); } -static void pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { struct pcf857x *gpio = gpiochip_get_data(chip); + int status; mutex_lock(&gpio->lock); gpio->out &= ~*mask; gpio->out |= *bits & *mask; - gpio->write(gpio->client, gpio->out); + status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); + + return status; } /*-------------------------------------------------------------------------*/ @@ -292,8 +295,8 @@ static int pcf857x_probe(struct i2c_client *client) gpio->chip.owner = THIS_MODULE; gpio->chip.get = pcf857x_get; gpio->chip.get_multiple = pcf857x_get_multiple; - gpio->chip.set = pcf857x_set; - gpio->chip.set_multiple = pcf857x_set_multiple; + gpio->chip.set_rv = pcf857x_set; + gpio->chip.set_multiple_rv = pcf857x_set_multiple; gpio->chip.direction_input = pcf857x_input; gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client); -- GitLab From 74260fb09cc6f6e7bb6741bedca4463079800ead Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:16 +0200 Subject: [PATCH 211/868] gpio: pch: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-3-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pch.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 63f25c72eac2f..c6f313342ba06 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -99,7 +99,7 @@ struct pch_gpio { spinlock_t spinlock; }; -static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +static int pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { u32 reg_val; struct pch_gpio *chip = gpiochip_get_data(gpio); @@ -114,6 +114,8 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) iowrite32(reg_val, &chip->reg->po); spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; } static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) @@ -217,7 +219,7 @@ static void pch_gpio_setup(struct pch_gpio *chip) gpio->direction_input = pch_gpio_direction_input; gpio->get = pch_gpio_get; gpio->direction_output = pch_gpio_direction_output; - gpio->set = pch_gpio_set; + gpio->set_rv = pch_gpio_set; gpio->base = -1; gpio->ngpio = gpio_pins[chip->ioh]; gpio->can_sleep = false; -- GitLab From 201e0f24a5b73fa73606ae2261d155f00cb6d577 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:17 +0200 Subject: [PATCH 212/868] gpio: pl061: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-4-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pl061.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 1c273727ffa3a..98cfac4eac852 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -115,11 +115,13 @@ static int pl061_get_value(struct gpio_chip *gc, unsigned offset) return !!readb(pl061->base + (BIT(offset + 2))); } -static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) +static int pl061_set_value(struct gpio_chip *gc, unsigned int offset, int value) { struct pl061 *pl061 = gpiochip_get_data(gc); writeb(!!value << offset, pl061->base + (BIT(offset + 2))); + + return 0; } static int pl061_irq_type(struct irq_data *d, unsigned trigger) @@ -328,7 +330,7 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id) pl061->gc.direction_input = pl061_direction_input; pl061->gc.direction_output = pl061_direction_output; pl061->gc.get = pl061_get_value; - pl061->gc.set = pl061_set_value; + pl061->gc.set_rv = pl061_set_value; pl061->gc.ngpio = PL061_GPIO_NR; pl061->gc.label = dev_name(dev); pl061->gc.parent = dev; -- GitLab From fecdef830c442e4375a3bbdde26af385583fb05c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:18 +0200 Subject: [PATCH 213/868] gpio: pmic-eic-sprd: drop unneeded .set() callback The lines on this chip are input-only. GPIO core can handle the missing .set() callback so there's no need to implement a dummy here. Drop it. Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-5-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pmic-eic-sprd.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index d9b228bea42ee..cb015fb5c9467 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -109,12 +109,6 @@ static int sprd_pmic_eic_direction_input(struct gpio_chip *chip, return 0; } -static void sprd_pmic_eic_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - /* EICs are always input, nothing need to do here. */ -} - static int sprd_pmic_eic_set_debounce(struct gpio_chip *chip, unsigned int offset, unsigned int debounce) @@ -351,7 +345,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) pmic_eic->chip.request = sprd_pmic_eic_request; pmic_eic->chip.free = sprd_pmic_eic_free; pmic_eic->chip.set_config = sprd_pmic_eic_set_config; - pmic_eic->chip.set = sprd_pmic_eic_set; pmic_eic->chip.get = sprd_pmic_eic_get; pmic_eic->chip.can_sleep = true; -- GitLab From f1ff31c8ef80f4720ebf7854e6af6204dddd9ce3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:19 +0200 Subject: [PATCH 214/868] gpio: pxa: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-6-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pxa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 131ab79ebce75..13f7da2a94864 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -315,12 +315,14 @@ static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(gplr & GPIO_bit(offset)); } -static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int pxa_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { void __iomem *base = gpio_bank_base(chip, offset); writel_relaxed(GPIO_bit(offset), base + (value ? GPSR_OFFSET : GPCR_OFFSET)); + + return 0; } #ifdef CONFIG_OF_GPIO @@ -353,7 +355,7 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, void __iom pchip->chip.direction_input = pxa_gpio_direction_input; pchip->chip.direction_output = pxa_gpio_direction_output; pchip->chip.get = pxa_gpio_get; - pchip->chip.set = pxa_gpio_set; + pchip->chip.set_rv = pxa_gpio_set; pchip->chip.to_irq = pxa_gpio_to_irq; pchip->chip.ngpio = ngpio; pchip->chip.request = gpiochip_generic_request; -- GitLab From 309ea5811136a06f572c7abbeae2dcf773629a5e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:20 +0200 Subject: [PATCH 215/868] gpio: rc5t583: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-7-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rc5t583.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-rc5t583.c b/drivers/gpio/gpio-rc5t583.c index c34dcadaee36d..cf3e91d235df2 100644 --- a/drivers/gpio/gpio-rc5t583.c +++ b/drivers/gpio/gpio-rc5t583.c @@ -35,14 +35,20 @@ static int rc5t583_gpio_get(struct gpio_chip *gc, unsigned int offset) return !!(val & BIT(offset)); } -static void rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int rc5t583_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct rc5t583_gpio *rc5t583_gpio = gpiochip_get_data(gc); struct device *parent = rc5t583_gpio->rc5t583->dev; + int ret; + if (val) - rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOOUT, + BIT(offset)); else - rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, BIT(offset)); + ret = rc5t583_clear_bits(parent, RC5T583_GPIO_IOOUT, + BIT(offset)); + + return ret; } static int rc5t583_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) @@ -66,7 +72,10 @@ static int rc5t583_gpio_dir_output(struct gpio_chip *gc, unsigned offset, struct device *parent = rc5t583_gpio->rc5t583->dev; int ret; - rc5t583_gpio_set(gc, offset, value); + ret = rc5t583_gpio_set(gc, offset, value); + if (ret) + return ret; + ret = rc5t583_set_bits(parent, RC5T583_GPIO_IOSEL, BIT(offset)); if (ret < 0) return ret; @@ -109,7 +118,7 @@ static int rc5t583_gpio_probe(struct platform_device *pdev) rc5t583_gpio->gpio_chip.free = rc5t583_gpio_free, rc5t583_gpio->gpio_chip.direction_input = rc5t583_gpio_dir_input, rc5t583_gpio->gpio_chip.direction_output = rc5t583_gpio_dir_output, - rc5t583_gpio->gpio_chip.set = rc5t583_gpio_set, + rc5t583_gpio->gpio_chip.set_rv = rc5t583_gpio_set, rc5t583_gpio->gpio_chip.get = rc5t583_gpio_get, rc5t583_gpio->gpio_chip.to_irq = rc5t583_gpio_to_irq, rc5t583_gpio->gpio_chip.ngpio = RC5T583_MAX_GPIO, -- GitLab From 76033f1f927cd93907c1e9eb3f86e54015e4366d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:21 +0200 Subject: [PATCH 216/868] gpio: rdc321x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-8-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rdc321x.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index ec7fb9220a474..a75ed8021de57 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -64,8 +64,8 @@ static void rdc_gpio_set_value_impl(struct gpio_chip *chip, } /* set GPIO pin to value */ -static void rdc_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int rdc_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, + int value) { struct rdc321x_gpio *gpch; @@ -73,6 +73,8 @@ static void rdc_gpio_set_value(struct gpio_chip *chip, spin_lock(&gpch->lock); rdc_gpio_set_value_impl(chip, gpio, value); spin_unlock(&gpch->lock); + + return 0; } static int rdc_gpio_config(struct gpio_chip *chip, @@ -157,7 +159,7 @@ static int rdc321x_gpio_probe(struct platform_device *pdev) rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; - rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; + rdc321x_gpio_dev->chip.set_rv = rdc_gpio_set_value; rdc321x_gpio_dev->chip.base = 0; rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; -- GitLab From 6731ad96e8770595d4a6ceb07e587b07d22da110 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:23 +0200 Subject: [PATCH 217/868] gpio: rockchip: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-10-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rockchip.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 64700a003f9a1..ecd60ff9e1dd1 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -177,8 +177,8 @@ static int rockchip_gpio_set_direction(struct gpio_chip *chip, return 0; } -static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct rockchip_pin_bank *bank = gpiochip_get_data(gc); unsigned long flags; @@ -186,6 +186,8 @@ static void rockchip_gpio_set(struct gpio_chip *gc, unsigned int offset, raw_spin_lock_irqsave(&bank->slock, flags); rockchip_gpio_writel_bit(bank, offset, value, bank->gpio_regs->port_dr); raw_spin_unlock_irqrestore(&bank->slock, flags); + + return 0; } static int rockchip_gpio_get(struct gpio_chip *gc, unsigned int offset) @@ -325,7 +327,7 @@ static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) static const struct gpio_chip rockchip_gpiolib_chip = { .request = gpiochip_generic_request, .free = gpiochip_generic_free, - .set = rockchip_gpio_set, + .set_rv = rockchip_gpio_set, .get = rockchip_gpio_get, .get_direction = rockchip_gpio_get_direction, .direction_input = rockchip_gpio_direction_input, -- GitLab From d68ddf651dd2faea5cdc6f6656148b739c38645b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:24 +0200 Subject: [PATCH 218/868] gpio: rtd: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-11-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rtd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c index bf7f008f58d70..25bbd749b019b 100644 --- a/drivers/gpio/gpio-rtd.c +++ b/drivers/gpio/gpio-rtd.c @@ -275,7 +275,7 @@ static int rtd_gpio_set_config(struct gpio_chip *chip, unsigned int offset, } } -static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct rtd_gpio *data = gpiochip_get_data(chip); u32 mask = BIT(offset % 32); @@ -292,6 +292,8 @@ static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) else val &= ~mask; writel_relaxed(val, data->base + dato_reg_offset); + + return 0; } static int rtd_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -563,7 +565,7 @@ static int rtd_gpio_probe(struct platform_device *pdev) data->gpio_chip.get_direction = rtd_gpio_get_direction; data->gpio_chip.direction_input = rtd_gpio_direction_input; data->gpio_chip.direction_output = rtd_gpio_direction_output; - data->gpio_chip.set = rtd_gpio_set; + data->gpio_chip.set_rv = rtd_gpio_set; data->gpio_chip.get = rtd_gpio_get; data->gpio_chip.set_config = rtd_gpio_set_config; data->gpio_chip.parent = dev; -- GitLab From cb908f3699fb137e28017a8fdf506c35762b3eb6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 19 Jun 2025 10:33:25 +0200 Subject: [PATCH 219/868] gpio: sa1100: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250619-gpiochip-set-rv-gpio-v2-12-74abf689fbd8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sa1100.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 3f3ee36bc3cb1..e9d054d78ccb8 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -43,11 +43,14 @@ static int sa1100_gpio_get(struct gpio_chip *chip, unsigned offset) BIT(offset); } -static void sa1100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int sa1100_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { int reg = value ? R_GPSR : R_GPCR; writel_relaxed(BIT(offset), sa1100_gpio_chip(chip)->membase + reg); + + return 0; } static int sa1100_get_direction(struct gpio_chip *chip, unsigned offset) @@ -96,7 +99,7 @@ static struct sa1100_gpio_chip sa1100_gpio_chip = { .get_direction = sa1100_get_direction, .direction_input = sa1100_direction_input, .direction_output = sa1100_direction_output, - .set = sa1100_gpio_set, + .set_rv = sa1100_gpio_set, .get = sa1100_gpio_get, .to_irq = sa1100_to_irq, .base = 0, -- GitLab From 8b04b766714e93ca5a8021ff3408c9ef89d9eb85 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Fri, 20 Jun 2025 17:38:42 +0530 Subject: [PATCH 220/868] ASoC: amd: acp: add soundwire machine for ACP7.0 and ACP7.1 sof stack Add SoundWire machine with RT722 multi functional codec support for ACP7.0 and ACP7.1 platforms at sdw link0 for sof stack. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20250620120942.1168827-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/Kconfig | 1 + sound/soc/amd/acp/amd-acp70-acpi-match.c | 12 ++++++++++++ sound/soc/amd/mach-config.h | 1 + 3 files changed, 14 insertions(+) diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index b9432052c6383..c2a60bc80ee61 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -148,6 +148,7 @@ config SND_SOC_AMD_SOF_SDW_MACH select SND_SOC_RT1316_SDW select SND_SOC_RT715_SDW select SND_SOC_RT715_SDCA_SDW + select SND_SOC_RT722_SDCA_SDW help This option enables SOF sound card support for SoundWire enabled AMD platforms along with ACP PDM controller. diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c index e87ccfeee5bdf..dcecac792e6df 100644 --- a/sound/soc/amd/acp/amd-acp70-acpi-match.c +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -155,6 +155,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines); +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = { + { + .link_mask = BIT(0), + .links = acp70_rt722_only, + .drv_name = "amd_sof_sdw", + .sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg", + .fw_filename = "sof-acp_7_0.ri", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines); + MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index fdf016a64bbf0..5b6362103ca0a 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -27,6 +27,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[]; struct config_entry { u32 flags; -- GitLab From 59566923d955b69bfb1e1163f07dff437dde8c9c Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Fri, 20 Jun 2025 17:38:43 +0530 Subject: [PATCH 221/868] ASoC: SOF: amd: add alternate machines for ACP7.0 and ACP7.1 platforms Add SoundWire machines as alternate machines for ACP7.0 and ACP7.1 platforms. Signed-off-by: Vijendar Mukunda Link: https://patch.msgid.link/20250620120942.1168827-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/pci-acp70.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sof/amd/pci-acp70.c b/sound/soc/sof/amd/pci-acp70.c index c4db216682522..51d36d43c42bd 100644 --- a/sound/soc/sof/amd/pci-acp70.c +++ b/sound/soc/sof/amd/pci-acp70.c @@ -48,6 +48,7 @@ static const struct sof_amd_acp_desc acp70_chip_info = { static const struct sof_dev_desc acp70_desc = { .machines = snd_soc_acpi_amd_acp70_sof_machines, + .alt_machines = snd_soc_acpi_amd_acp70_sof_sdw_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, -- GitLab From a6c05c2e6871c94b349703c1ec8584763797fd8e Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:41 +0200 Subject: [PATCH 222/868] dt-bindings: regulator: mediatek-dvfsrc: Add MT6893 support Add a compatible for the MediaTek Dimensity 1200 (MT6893) SoC's regulators over DVFSRC. This SoC uses different values for the vsel, hence it is not compatible with the currently supported ones. Signed-off-by: AngeloGioacchino Del Regno Acked-by: "Rob Herring (Arm)" Link: https://patch.msgid.link/20250623120144.109359-2-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index 704828687970e..acac5c869f2c4 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - mediatek,mt6873-dvfsrc-regulator + - mediatek,mt6893-dvfsrc-regulator - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator -- GitLab From 7aafbb463be85472dc6db194ab9df45fd497c998 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:42 +0200 Subject: [PATCH 223/868] regulator: mtk-dvfsrc: Add support for Dimensity 1200 MT6893 The MediaTek Dimensity 1200 (MT6893) features the same DVFSRC regulators as the other currently supported SoCs, but with a different select value: add an array describing the possible voltages for the VCORE and VSCP regulators, and assign it to a new compatible for this SoC. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-3-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/regulator/mtk-dvfsrc-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c index f5662c5694642..5bc4d85517f0d 100644 --- a/drivers/regulator/mtk-dvfsrc-regulator.c +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -117,6 +117,24 @@ static const struct dvfsrc_regulator_pdata mt6873_data = { .size = ARRAY_SIZE(mt6873_regulators), }; +static const unsigned int mt6893_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 750000, +}; + +static const struct regulator_desc mt6893_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt6893_voltages), + MTK_DVFSRC_VREG("dvfsrc-vscp", VSCP, mt6893_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt6893_data = { + .descs = mt6893_regulators, + .size = ARRAY_SIZE(mt6893_regulators), +}; + static const unsigned int mt8183_voltages[] = { 725000, 800000, @@ -173,6 +191,7 @@ static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) static const struct of_device_id mtk_dvfsrc_regulator_match[] = { { .compatible = "mediatek,mt6873-dvfsrc-regulator", .data = &mt6873_data }, + { .compatible = "mediatek,mt6893-dvfsrc-regulator", .data = &mt6893_data }, { .compatible = "mediatek,mt8183-dvfsrc-regulator", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-dvfsrc-regulator", .data = &mt6873_data }, { .compatible = "mediatek,mt8195-dvfsrc-regulator", .data = &mt8195_data }, -- GitLab From ae77b8e8b0326818bd170818b37d055aa57a1569 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:43 +0200 Subject: [PATCH 224/868] dt-bindings: regulator: mediatek-dvfsrc: Add MT8196 support Add a compatible for the MediaTek MT8196 Chromebook SoC's regulators over DVFSRC. This SoC has only one regulator "dvfsrc-vcore" and different values for vsel compared to the others. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-4-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index acac5c869f2c4..685ccf9cf4d48 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -21,6 +21,7 @@ properties: - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator + - mediatek,mt8196-dvfsrc-regulator dvfsrc-vcore: description: DVFSRC-controlled SoC Vcore regulator -- GitLab From 024f39fff6d222cedde361f7fe34d9ba4e6afb92 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:44 +0200 Subject: [PATCH 225/868] regulator: mtk-dvfsrc: Add support for MediaTek MT8196 DVFSRC The MediaTek MT8196 Chromebook SoC features one DVFSRC regulator with 6 voltage steps. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-5-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/regulator/mtk-dvfsrc-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c index 5bc4d85517f0d..c0c9a6751c26b 100644 --- a/drivers/regulator/mtk-dvfsrc-regulator.c +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -166,6 +166,24 @@ static const struct dvfsrc_regulator_pdata mt8195_data = { .size = ARRAY_SIZE(mt8195_regulators), }; +static const unsigned int mt8196_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 825000, + 875000, +}; + +static const struct regulator_desc mt8196_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt8196_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt8196_data = { + .descs = mt8196_regulators, + .size = ARRAY_SIZE(mt8196_regulators), +}; + static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) { struct regulator_config config = { .dev = &pdev->dev }; @@ -195,6 +213,7 @@ static const struct of_device_id mtk_dvfsrc_regulator_match[] = { { .compatible = "mediatek,mt8183-dvfsrc-regulator", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-dvfsrc-regulator", .data = &mt6873_data }, { .compatible = "mediatek,mt8195-dvfsrc-regulator", .data = &mt8195_data }, + { .compatible = "mediatek,mt8196-dvfsrc-regulator", .data = &mt8196_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match); -- GitLab From d5363522042bca867f45174b4d5a8b5a39eae989 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 23 Jun 2025 13:38:54 +0200 Subject: [PATCH 226/868] ALSA: mixer_oss: Remove deprecated strcpy() function calls Remove the deprecated strcpy() function calls and assign the strings directly to a 'char *' instead. Use 'if/else if' instead of two separate if statements. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250623113855.37031-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/core/oss/mixer_oss.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 05fc8911479c1..e839a4bb93f81 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -991,7 +991,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct slot *pslot; struct snd_kcontrol *kctl; struct snd_mixer_oss_slot *rslot; - char str[64]; + const char *str; /* check if already assigned */ if (mixer->slots[ptr->oss_id].get_volume && ! replace_old) @@ -1014,11 +1014,11 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, if (kctl->info(kctl, uinfo)) return 0; - strcpy(str, ptr->name); + str = ptr->name; if (!strcmp(str, "Master")) - strcpy(str, "Mix"); - if (!strcmp(str, "Master Mono")) - strcpy(str, "Mix Mono"); + str = "Mix"; + else if (!strcmp(str, "Master Mono")) + str = "Mix Mono"; slot.capture_item = 0; if (!strcmp(uinfo->value.enumerated.name, str)) { slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; -- GitLab From 1adcbdf54f76e1004bdf71df4eb1888c26e7ad06 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Jun 2025 15:14:30 +0200 Subject: [PATCH 227/868] ALSA: hda: Disable jack polling at shutdown Although the jack polling is canceled at shutdown in snd_hda_codec_shutdown(), it might be still re-triggered when the work is being processed at cancel_delayed_work_sync() call. This may result in the unexpected hardware access that should have been already disabled. For assuring to stop the jack polling, clear codec->jackpoll_interval at shutdown. Reported-by: Joakim Zhang Closes: https://lore.kernel.org/20250619020844.2974160-4-joakim.zhang@cixtech.com Tested-by: Joakim Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250623131437.10670-2-tiwai@suse.de --- sound/pci/hda/hda_codec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c018beeecd3d6..4eba3970cd1a7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3020,6 +3020,7 @@ void snd_hda_codec_shutdown(struct hda_codec *codec) if (!codec->core.registered) return; + codec->jackpoll_interval = 0; /* don't poll any longer */ cancel_delayed_work_sync(&codec->jackpoll_work); list_for_each_entry(cpcm, &codec->pcm_list_head, list) snd_pcm_suspend_all(cpcm->pcm); -- GitLab From 507cd1216a6d70da6b230c806862c065632c713f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Jun 2025 15:14:31 +0200 Subject: [PATCH 228/868] ALSA: hda: Disable codec runtime PM when jack polling is enabled When the jack polling is triggered quite frequently, it makes little sense to perform the runtime PM at each time; since we do almost full configuration at each runtime resume for HD-audio, the merit by power saving would be lost. Hence, it'd be more reasonable to disable the runtime PM when the jack polling is enabled. This patch introduces the runtime PM idle callback and disables the runtime PM when the jack polling is enabled. This also serves as the preliminary change for the further jack poll cleanup. The exception is the case where codec->bus->jackpoll_in_suspend flag is set by the driver (currently set by HD-audio Tegra driver). This flag indicates that the polling is infrequent, hence there is still benefit to perform the runtime PM. The idle callback checks this flag and allows the runtime PM when set. Link: https://lore.kernel.org/20250619020844.2974160-1-joakim.zhang@cixtech.com Tested-by: Joakim Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250623131437.10670-3-tiwai@suse.de --- sound/pci/hda/hda_codec.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 4eba3970cd1a7..ea361390b8293 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2943,6 +2943,15 @@ static int hda_codec_runtime_resume(struct device *dev) return 0; } +static int hda_codec_runtime_idle(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend) + return -EBUSY; + return 0; +} + static int hda_codec_pm_prepare(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); @@ -3008,7 +3017,8 @@ const struct dev_pm_ops hda_codec_driver_pm = { .thaw = pm_sleep_ptr(hda_codec_pm_thaw), .poweroff = pm_sleep_ptr(hda_codec_pm_suspend), .restore = pm_sleep_ptr(hda_codec_pm_restore), - RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) + RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, + hda_codec_runtime_idle) }; /* suspend the codec at shutdown; called from driver's shutdown callback */ -- GitLab From 5f7e54b23e4d253eff3b10b12d6fa92d28d7dddc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Jun 2025 15:14:32 +0200 Subject: [PATCH 229/868] ALSA: hda: Handle the jack polling always via a work We used to call directly hda_jackpoll_work() from a couple of places for updating the jack and notify to user-space, but this makes rather the code flow fragile. Namely, because of those direct calls, hda_jackpoll_work() uses snd_hda_power_up_pm() and *_down_pm() calls instead of the standard snd_hda_power_up() and *_down() calls. The latter pair assures the runtime PM resume sync, so it can avoid the race against the PM callbacks gracefully, while the former pair may continue if called concurrently, hence it may race (by design). In this patch, we change the call pattern of hda_jackpoll_work(); now all callers are replaced with the standard snd_hda_jack_report_sync() and the additional schedule_delayed_work(). Since hda_jackpoll_work() is called only from the associated work, it's always outside the PM code path, and we can safely use snd_hda_power_up() and *_down() there instead. This allows us to remove the racy check of power-state in hda_jackpoll_work(), as well as the tricky cancel_delayed_work() and rescheduling at hda_codec_runtime_suspend(). Reported-by: Joakim Zhang Closes: https://lore.kernel.org/20250619020844.2974160-1-joakim.zhang@cixtech.com Tested-by: Joakim Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250623131437.10670-4-tiwai@suse.de --- sound/pci/hda/hda_codec.c | 41 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ea361390b8293..5508381a18332 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -639,24 +639,16 @@ static void hda_jackpoll_work(struct work_struct *work) struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - /* for non-polling trigger: we need nothing if already powered on */ - if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) + if (!codec->jackpoll_interval) return; /* the power-up/down sequence triggers the runtime resume */ - snd_hda_power_up_pm(codec); + snd_hda_power_up(codec); /* update jacks manually if polling is required, too */ - if (codec->jackpoll_interval) { - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_poll_all(codec); - } - snd_hda_power_down_pm(codec); - - if (!codec->jackpoll_interval) - return; - - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_poll_all(codec); + schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); + snd_hda_power_down(codec); } /* release all pincfg lists */ @@ -2895,12 +2887,12 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_regmap_sync(codec); } - if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); + snd_hda_jack_report_sync(codec); codec->core.dev.power.power_state = PMSG_ON; snd_hdac_leave_pm(&codec->core); + if (codec->jackpoll_interval) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } static int hda_codec_runtime_suspend(struct device *dev) @@ -2912,8 +2904,6 @@ static int hda_codec_runtime_suspend(struct device *dev) if (!codec->card) return 0; - cancel_delayed_work_sync(&codec->jackpoll_work); - state = hda_call_codec_suspend(codec); if (codec->link_down_at_suspend || (codec_has_clkstop(codec) && codec_has_epss(codec) && @@ -2921,10 +2911,6 @@ static int hda_codec_runtime_suspend(struct device *dev) snd_hdac_codec_link_down(&codec->core); snd_hda_codec_display_power(codec, false); - if (codec->bus->jackpoll_in_suspend && - (dev->power.power_state.event != PM_EVENT_SUSPEND)) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); return 0; } @@ -3097,10 +3083,11 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) if (err < 0) return err; + snd_hda_jack_report_sync(codec); /* call at the last init point */ if (codec->jackpoll_interval) - hda_jackpoll_work(&codec->jackpoll_work.work); - else - snd_hda_jack_report_sync(codec); /* call at the last init point */ + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + sync_power_up_states(codec); return 0; } -- GitLab From 17cc308b183308bf5ada36e164284fff7eb064ba Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 20 Jun 2025 10:14:03 +0800 Subject: [PATCH 230/868] ASoC: wm8524: enable constraints when sysclk is configured. In some cases, the sysclk won't be configured on init, and sysclk can be changed in hw_params() according to different sample rate, for example, for 44kHz sample rate, the sysclk is 11.2896MHz, for 48kHz sample rate, the sysclk is 12.288MHz. In order to support the above case, only enable constraints when sysclk is configured, and check the rate in hw_params. So overall there are three cases that need to be considered: - call set_sysclk() on init, then constraints will be initialized. - don't call set_sysclk() on init, but call it after startup(), then constraints will be configured, the constraints can be cleared with call set_sysclk() again in shutdown(). - don't call set_sysclk() in the whole flow, then there are no any constraints. The clocks depend on cpu dai. Enlarge the WM8524_NUM_RATES to 12, as the supported rate range is 8kHz to 192kHz. Signed-off-by: Shengjiu Wang Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250620021403.624303-1-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm8524.c | 55 ++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c index 403e513f3fa8b..6b1a7450b0acc 100644 --- a/sound/soc/codecs/wm8524.c +++ b/sound/soc/codecs/wm8524.c @@ -21,7 +21,7 @@ #include #include -#define WM8524_NUM_RATES 7 +#define WM8524_NUM_RATES 12 /* codec private data */ struct wm8524_priv { @@ -46,7 +46,7 @@ static const struct snd_soc_dapm_route wm8524_dapm_routes[] = { static const struct { int value; int ratio; -} lrclk_ratios[WM8524_NUM_RATES] = { +} lrclk_ratios[] = { { 1, 128 }, { 2, 192 }, { 3, 256 }, @@ -63,17 +63,12 @@ static int wm8524_startup(struct snd_pcm_substream *substream, struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); /* The set of sample rates that can be supported depends on the - * MCLK supplied to the CODEC - enforce this. + * MCLK supplied to the CODEC. */ - if (!wm8524->sysclk) { - dev_err(component->dev, - "No MCLK configured, call set_sysclk() on init\n"); - return -EINVAL; - } - - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &wm8524->rate_constraint); + if (wm8524->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &wm8524->rate_constraint); gpiod_set_value_cansleep(wm8524->mute, 1); @@ -97,9 +92,11 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, unsigned int val; int i, j = 0; + wm8524->rate_constraint.count = 0; wm8524->sysclk = freq; + if (!wm8524->sysclk) + return 0; - wm8524->rate_constraint.count = 0; for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { val = freq / lrclk_ratios[i].ratio; /* Check that it's a standard rate since core can't @@ -108,9 +105,13 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, */ switch (val) { case 8000: + case 11025: + case 16000: + case 22050: case 32000: case 44100: case 48000: + case 64000: case 88200: case 96000: case 176400: @@ -157,6 +158,33 @@ static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream) return 0; } +static int wm8524_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + int i; + + /* If sysclk is not configured, no need to check the rate */ + if (!wm8524->sysclk) + return 0; + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8524->rate_constraint.count; i++) { + if (wm8524->rate_constraint.list[i] == params_rate(params)) + break; + } + + if (i == wm8524->rate_constraint.count) { + dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8524->sysclk); + return -EINVAL; + } + + return 0; +} + #define WM8524_RATES SNDRV_PCM_RATE_8000_192000 #define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ @@ -169,6 +197,7 @@ static const struct snd_soc_dai_ops wm8524_dai_ops = { .set_sysclk = wm8524_set_dai_sysclk, .set_fmt = wm8524_set_fmt, .mute_stream = wm8524_mute_stream, + .hw_params = wm8524_hw_params, }; static struct snd_soc_dai_driver wm8524_dai = { -- GitLab From 76f03ce1c6f22805ecf689b1f3ecfb56582eddd5 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 20 Jun 2025 14:28:24 +0100 Subject: [PATCH 231/868] spi: microchip-core-qspi: set min_speed_hz during probe The controller's minimum possible bus clock is 1/30 the rate of the input clock. Naively set the minimum bus clock speed the controller is capable of during probe, assuming that the rate will never reduce further. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-drained-widen-ac311bd5f172@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index fa828fcaaef2d..111ae6519ff41 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -562,6 +562,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; + ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) -- GitLab From 75ca45c472dac206df2ebbc1c0f1f9c3bbdace50 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 20 Jun 2025 14:28:25 +0100 Subject: [PATCH 232/868] spi: microchip-core-qspi: remove unused param from mchp_coreqspi_write_op() "word" is unused in mchp_coreqspi_write_op(), so delete it. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-starry-excusably-25e6be957d9d@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index 111ae6519ff41..67ff5f8aa84d0 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -194,7 +194,7 @@ static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) } } -static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) +static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) { u32 control, data; @@ -415,7 +415,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->tx_len = op->cmd.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } qspi->txbuf = &opaddr[0]; @@ -426,7 +426,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->tx_len = op->addr.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } if (op->data.nbytes) { @@ -435,7 +435,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o qspi->rxbuf = NULL; qspi->rx_len = 0; qspi->tx_len = op->data.nbytes; - mchp_coreqspi_write_op(qspi, true); + mchp_coreqspi_write_op(qspi); } else { qspi->txbuf = NULL; qspi->rxbuf = (u8 *)op->data.buf.in; -- GitLab From 8f9cf02c8852837923f1cdacfcc92e138513325c Mon Sep 17 00:00:00 2001 From: Cyril Jean Date: Fri, 20 Jun 2025 14:28:26 +0100 Subject: [PATCH 233/868] spi: microchip-core-qspi: Add regular transfers The driver for CoreQSPI only supports memory operations at present, so add support for regular transfers so that the SD card slot and ADC on the BeagleV Fire can be used. Signed-off-by: Cyril Jean Co-developed-by: Conor Dooley Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20250620-splice-shelter-310771564886@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 217 +++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index 67ff5f8aa84d0..d13a9b755c7f8 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -222,6 +222,87 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) } } +static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi) +{ + u32 control, data; + + qspi->rx_len = qspi->tx_len; + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control |= CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + + data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xaa; + if (qspi->txbuf) + qspi->txbuf += 4; + qspi->tx_len -= 4; + writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); + + /* + * The rx FIFO is twice the size of the tx FIFO, so there is + * no requirement to block transmission if receive data is not + * ready, and it is fine to let the tx FIFO completely fill + * without reading anything from the rx FIFO. Once the tx FIFO + * has been filled and becomes non-full due to a transmission + * occurring there will always be something to receive. + * IOW, this is safe as TX_FIFO_SIZE + 4 < 2 * TX_FIFO_SIZE + */ + if (qspi->rx_len >= 4) { + if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) { + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + } + } + + /* + * Since transmission is not being blocked by clearing the rx FIFO, + * loop here until all received data "leaked" by the loop above has + * been dealt with. + */ + while (qspi->rx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + + /* + * Since rx_len and tx_len must be < 4 bytes at this point, there's no + * concern about overflowing the rx or tx FIFOs any longer. It's + * therefore safe to loop over the remainder of the transmit data before + * handling the remaining receive data. + */ + if (!qspi->tx_len) + return; + + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + data = qspi->txbuf ? *qspi->txbuf : 0xaa; + qspi->txbuf++; + writel_relaxed(data, qspi->regs + REG_TX_DATA); + } + + while (qspi->rx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_RX_DATA); + *qspi->rxbuf++ = (data & 0xFF); + } +} + static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) { u32 mask = IEN_TXDONE | @@ -266,7 +347,7 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id) } static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi, - const struct spi_mem_op *op) + u32 max_freq) { unsigned long clk_hz; u32 control, baud_rate_val = 0; @@ -275,11 +356,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi if (!clk_hz) return -EINVAL; - baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq); + baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * max_freq); if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) { dev_err(&spi->dev, "could not configure the clock for spi clock %d Hz & system clock %ld Hz\n", - op->max_freq, clk_hz); + max_freq, clk_hz); return -EINVAL; } @@ -367,23 +448,13 @@ static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const str writel_relaxed(frames, qspi->regs + REG_FRAMES); } -static int mchp_qspi_wait_for_ready(struct spi_mem *mem) +static int mchp_coreqspi_wait_for_ready(struct mchp_coreqspi *qspi) { - struct mchp_coreqspi *qspi = spi_controller_get_devdata - (mem->spi->controller); u32 status; - int ret; - ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, + return readl_poll_timeout(qspi->regs + REG_STATUS, status, (status & STATUS_READY), 0, TIMEOUT_MS); - if (ret) { - dev_err(&mem->spi->dev, - "Timeout waiting on QSPI ready.\n"); - return -ETIMEDOUT; - } - - return ret; } static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) @@ -396,11 +467,13 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o int err, i; mutex_lock(&qspi->op_lock); - err = mchp_qspi_wait_for_ready(mem); - if (err) + err = mchp_coreqspi_wait_for_ready(qspi); + if (err) { + dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n"); goto error; + } - err = mchp_coreqspi_setup_clock(qspi, mem->spi, op); + err = mchp_coreqspi_setup_clock(qspi, mem->spi, op->max_freq); if (err) goto error; @@ -515,6 +588,109 @@ static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = { .per_op_freq = true, }; +static int mchp_coreqspi_unprepare_message(struct spi_controller *ctlr, struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + /* + * This delay is required for the driver to function correctly, + * but no explanation has been determined for why it is required. + */ + udelay(750); + + mutex_unlock(&qspi->op_lock); + + return 0; +} + +static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + struct spi_transfer *t = NULL; + u32 control, frames; + u32 total_bytes = 0, cmd_bytes = 0, idle_cycles = 0; + int ret; + bool quad = false, dual = false; + + mutex_lock(&qspi->op_lock); + ret = mchp_coreqspi_wait_for_ready(qspi); + if (ret) { + mutex_unlock(&qspi->op_lock); + dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n"); + return ret; + } + + ret = mchp_coreqspi_setup_clock(qspi, m->spi, m->spi->max_speed_hz); + if (ret) { + mutex_unlock(&qspi->op_lock); + return ret; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + writel_relaxed(control, qspi->regs + REG_CONTROL); + + reinit_completion(&qspi->data_completion); + + list_for_each_entry(t, &m->transfers, transfer_list) { + total_bytes += t->len; + if (!cmd_bytes && !(t->tx_buf && t->rx_buf)) + cmd_bytes = t->len; + if (!t->rx_buf) + cmd_bytes = total_bytes; + if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD) + quad = true; + else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL) + dual = true; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (quad) { + control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW); + } else if (dual) { + control &= ~CONTROL_MODE0; + control |= CONTROL_MODE12_FULL; + } else { + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + } + writel_relaxed(control, qspi->regs + REG_CONTROL); + + frames = total_bytes & BYTESUPPER_MASK; + writel_relaxed(frames, qspi->regs + REG_FRAMESUP); + frames = total_bytes & BYTESLOWER_MASK; + frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT; + frames |= idle_cycles << FRAMES_IDLE_SHIFT; + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (control & CONTROL_MODE12_MASK) + frames |= (1 << FRAMES_SHIFT); + + frames |= FRAMES_FLAGWORD; + writel_relaxed(frames, qspi->regs + REG_FRAMES); + + return 0; +}; + +static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *t) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + qspi->tx_len = t->len; + + if (t->tx_buf) + qspi->txbuf = (u8 *)t->tx_buf; + + if (!t->rx_buf) { + mchp_coreqspi_write_op(qspi); + } else { + qspi->rxbuf = (u8 *)t->rx_buf; + qspi->rx_len = t->len; + mchp_coreqspi_write_read_op(qspi); + } + + return 0; +} + static int mchp_coreqspi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; @@ -563,6 +739,11 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30; + ctlr->prepare_message = mchp_coreqspi_prepare_message; + ctlr->unprepare_message = mchp_coreqspi_unprepare_message; + ctlr->transfer_one = mchp_coreqspi_transfer_one; + ctlr->num_chipselect = 2; + ctlr->use_gpio_descriptors = true; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) -- GitLab From 6469fb5c8b2dc88c3840eced9ccdd3ddc97213b2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:41:46 +0000 Subject: [PATCH 234/868] ASoC: ti: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874iwl20th.wl-kuninori.morimoto.gx@renesas.com Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/ti/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index e22e41af32269..3323cf96e3095 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "Audio support for Texas Instruments SoCs" +menu "Texas Instruments" depends on DMA_OMAP || TI_EDMA || TI_K3_UDMA || COMPILE_TEST config SND_SOC_TI_EDMA_PCM -- GitLab From 652dd81c7a66843db0afbe0866537492c965a3cc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:41:54 +0000 Subject: [PATCH 235/868] ASoC: adi: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/8734c520t9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/adi/Kconfig | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig index 0236dc5b4e9f0..d47dffbf40d06 100644 --- a/sound/soc/adi/Kconfig +++ b/sound/soc/adi/Kconfig @@ -1,12 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_ADI - tristate "Audio support for Analog Devices reference designs" - help - Audio support for various reference designs by Analog Devices. +menu "Analog Devices" config SND_SOC_ADI_AXI_I2S tristate "AXI-I2S support" - depends on SND_SOC_ADI select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help @@ -14,8 +10,9 @@ config SND_SOC_ADI_AXI_I2S config SND_SOC_ADI_AXI_SPDIF tristate "AXI-SPDIF support" - depends on SND_SOC_ADI select SND_SOC_GENERIC_DMAENGINE_PCM select REGMAP_MMIO help ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral. + +endmenu -- GitLab From 6895deb5f25f92a9a8f7cf52977b0e8ef18baebb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:00 +0000 Subject: [PATCH 236/868] ASoC: amd: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/871prp20t3.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index c7daae392d746..fd35a03aadcbe 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "AMD" + config SND_SOC_AMD_ACP tristate "AMD Audio Coprocessor support" help @@ -185,3 +187,4 @@ config SND_SOC_AMD_PS_MACH If unsure select "N". endif +endmenu -- GitLab From da65de541568cb9beb0feaa302a91d6af2f0258a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:08 +0000 Subject: [PATCH 237/868] ASoC: bcm: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87zfedzqfj.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/bcm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index 4218057b08742..de4e8a0daf1ce 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Broadcom" + config SND_BCM2835_SOC_I2S tristate "SoC Audio support for the Broadcom BCM2835 I2S module" depends on ARCH_BCM2835 || COMPILE_TEST @@ -26,3 +28,5 @@ config SND_BCM63XX_I2S_WHISTLER DSL/PON chips (bcm63158, bcm63178) If you don't know what to do here, say N + +endmenu -- GitLab From bb0b8820527eb32b5f9cc1405e855a33eca2d83b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:13 +0000 Subject: [PATCH 238/868] ASoC: dwc: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0txzqfe.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/dwc/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index 71a58f7ac13a9..6bb31b64210a4 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "DesignWare" + config SND_DESIGNWARE_I2S tristate "Synopsys I2S Device Driver" depends on HAVE_CLK @@ -18,3 +20,4 @@ config SND_DESIGNWARE_PCM This functionality is specially suited for I2S devices that don't have DMA support. +endmenu -- GitLab From 71951375b0ea03671a1752991cbeab0b9bc3f365 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:18 +0000 Subject: [PATCH 239/868] ASoC: fsl: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87wm9hzqf9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index c4cf3cff58ded..c4a00b22bc2a1 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "SoC Audio for Freescale CPUs" +menu "Freescale" comment "Common SoC Audio options for Freescale CPUs:" -- GitLab From b13f7eef9ff82e0163c6af8ac262fc2607161390 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:23 +0000 Subject: [PATCH 240/868] ASoC: img: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87v7p1zqf4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- arch/mips/configs/generic/board-marduk.config | 1 - sound/soc/img/Kconfig | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/mips/configs/generic/board-marduk.config b/arch/mips/configs/generic/board-marduk.config index 05ca34cd5a734..65433c5c4fdee 100644 --- a/arch/mips/configs/generic/board-marduk.config +++ b/arch/mips/configs/generic/board-marduk.config @@ -50,4 +50,3 @@ CONFIG_CRYPTO_DEV_IMGTEC_HASH=y CONFIG_IMGPDC_WDT=y CONFIG_IR_IMG=y CONFIG_CC10001_ADC=y -CONFIG_SND_SOC_IMG=y diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index 568efa606ca4b..9a4cba6fdb505 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -1,12 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_IMG - bool "Audio support for Imagination Technologies designs" - help - Audio support for Imagination Technologies audio hardware +menu "Imagination Technologies" config SND_SOC_IMG_I2S_IN tristate "Imagination I2S Input Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S in driver for @@ -14,7 +10,6 @@ config SND_SOC_IMG_I2S_IN config SND_SOC_IMG_I2S_OUT tristate "Imagination I2S Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S out driver for @@ -22,7 +17,6 @@ config SND_SOC_IMG_I2S_OUT config SND_SOC_IMG_PARALLEL_OUT tristate "Imagination Parallel Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for parallel out driver for @@ -30,7 +24,6 @@ config SND_SOC_IMG_PARALLEL_OUT config SND_SOC_IMG_SPDIF_IN tristate "Imagination SPDIF Input Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF input driver for @@ -38,7 +31,6 @@ config SND_SOC_IMG_SPDIF_IN config SND_SOC_IMG_SPDIF_OUT tristate "Imagination SPDIF Output Device Driver" - depends on SND_SOC_IMG select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF out driver for @@ -47,7 +39,8 @@ config SND_SOC_IMG_SPDIF_OUT config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC tristate "Support for Pistachio SoC Internal DAC Driver" - depends on SND_SOC_IMG help Say Y or M if you want to add support for Pistachio internal DAC driver for Imagination Technologies Pistachio internal DAC device. + +endmenu -- GitLab From 01f7d179a7348c252c5f26e1521bc1df81fadcd2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:28 +0000 Subject: [PATCH 241/868] ASoC: mxs: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87tt4lzqez.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mxs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index 402ef1ee7a325..2fb1ca711f71b 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_MXS_SOC - tristate "SoC Audio for Freescale MXS CPUs" + tristate "Freescale MXS" depends on ARCH_MXS || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM -- GitLab From bcd02da3721967c006c90b1b58536d34765283a5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:33 +0000 Subject: [PATCH 242/868] ASoC: pxa: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87sek5zqeu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e05d6ce4c8fa2..e026f9912a6d1 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "PXA" + config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA || COMPILE_TEST @@ -52,3 +54,5 @@ config SND_PXA910_SOC help Say Y if you want to add support for SoC audio on the Marvell PXA910 reference platform. + +endmenu -- GitLab From afd1bff53b48d1431fefe57887017207c3df6bf5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:38 +0000 Subject: [PATCH 243/868] ASoC: sti: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzzpzqep.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sti/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig index f881da4b6aead..8e12356a4490f 100644 --- a/sound/soc/sti/Kconfig +++ b/sound/soc/sti/Kconfig @@ -3,7 +3,7 @@ # STM SoC audio configuration # menuconfig SND_SOC_STI - tristate "SoC Audio support for STI System-On-Chip" + tristate "STI" depends on SND_SOC depends on ARCH_STI || COMPILE_TEST select SND_SOC_GENERIC_DMAENGINE_PCM -- GitLab From 53696514d72fab66025afca531a1668bee635abf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:43 +0000 Subject: [PATCH 244/868] ASoC: stm: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87plf9zqek.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/stm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig index da1f7a16605b4..2753d6c2a8266 100644 --- a/sound/soc/stm/Kconfig +++ b/sound/soc/stm/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "STMicroelectronics STM32 SOC audio support" +menu "STMicroelectronics STM32" config SND_SOC_STM32_SAI tristate "STM32 SAI interface (Serial Audio Interface) support" -- GitLab From 3b2a8a3c5769e2e02bf6f2cd7444847056087984 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:49 +0000 Subject: [PATCH 245/868] ASoC: sof: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6utzqef.wl-kuninori.morimoto.gx@renesas.com Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/Kconfig | 4 +++- sound/soc/sof/Kconfig | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 1b983c7006f18..ce74818bd7152 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -127,7 +127,6 @@ source "sound/soc/renesas/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sdca/Kconfig" -source "sound/soc/sof/Kconfig" source "sound/soc/spear/Kconfig" source "sound/soc/sprd/Kconfig" source "sound/soc/starfive/Kconfig" @@ -141,6 +140,9 @@ source "sound/soc/ux500/Kconfig" source "sound/soc/xilinx/Kconfig" source "sound/soc/xtensa/Kconfig" +# SOF +source "sound/soc/sof/Kconfig" + # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 32ffd345e07fc..a487ab0b51c7e 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_SOF_TOPLEVEL - bool "Sound Open Firmware Support" + bool "Sound Open Firmware (SOF) platforms" help This adds support for Sound Open Firmware (SOF). SOF is free and generic open source audio DSP firmware for multiple devices. -- GitLab From 335c898312f3b2366123c10b7bca56ca8cea1781 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:53 +0000 Subject: [PATCH 246/868] ASoC: au1x: Standardize ASoC menu use "ASoC support for xxxx" menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87msadzqea.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/au1x/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig index 8a78809e87543..a7630897bc0b3 100644 --- a/sound/soc/au1x/Kconfig +++ b/sound/soc/au1x/Kconfig @@ -2,6 +2,8 @@ ## ## Au1200/Au1550/Au1300 PSC + DBDMA ## +menu "Au1x" + config SND_SOC_AU1XPSC tristate "SoC Audio for Au12xx/Au13xx/Au1550" depends on MIPS_ALCHEMY @@ -63,3 +65,5 @@ config SND_SOC_DB1200 Select this option to enable audio (AC97 and I2S) on the Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards. If you need Db1300 touchscreen support, you definitely want to say Y. + +endmenu -- GitLab From 36e6420440db119943b28b84523b8e0e77cae08b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:42:58 +0000 Subject: [PATCH 247/868] ASoC: sdca: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ldpxzqe5.wl-kuninori.morimoto.gx@renesas.com Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/sdca/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index ee20b9914aa1f..71d2a52b2b50e 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "SoundWire (SDCA)" config SND_SOC_SDCA tristate @@ -9,3 +10,5 @@ config SND_SOC_SDCA config SND_SOC_SDCA_OPTIONAL def_tristate SND_SOC_SDCA || !SND_SOC_SDCA + +endmenu -- GitLab From 1d9ac30a8df639169ab97576cb5b1d48bb403e8b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:02 +0000 Subject: [PATCH 248/868] ASoC: sprd: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jz5hzqe1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sprd/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig index 5e0ac82785721..8ddfe55aa21f8 100644 --- a/sound/soc/sprd/Kconfig +++ b/sound/soc/sprd/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Spreadtrum" + config SND_SOC_SPRD tristate "SoC Audio for the Spreadtrum SoC chips" depends on ARCH_SPRD || COMPILE_TEST @@ -14,3 +16,5 @@ config SND_SOC_SPRD_MCDT Say y here to enable multi-channel data transfer support. It is used for sound stream transmission between audio subsystem and other AP/CP subsystem. + +endmenu -- GitLab From 8ada023e996ca7131d9a42b2dd915e1966870d4a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:07 +0000 Subject: [PATCH 249/868] ASoC: qcom: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ikl1zqdx.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index e86b4a03dd61d..dfbdab9eb3a39 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_QCOM - tristate "ASoC support for QCOM platforms" + tristate "Qualcomm" depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices -- GitLab From 58e490935f20e38a4de156f705e89f7f13838aad Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:11 +0000 Subject: [PATCH 250/868] ASoC: meson: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87h60lzqds.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 6458d5dc4902f..d9a730994a2a2 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "ASoC support for Amlogic platforms" +menu "Amlogic" depends on ARCH_MESON || (COMPILE_TEST && COMMON_CLK) config SND_MESON_AIU -- GitLab From 68a8fd461a564f8b5957944f4b056f44de5dfabc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:16 +0000 Subject: [PATCH 251/868] ASoC: apple: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87frg5zqdo.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/apple/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig index e9c777cdb6e39..d8dc2f1ccc83e 100644 --- a/sound/soc/apple/Kconfig +++ b/sound/soc/apple/Kconfig @@ -1,3 +1,5 @@ +menu "Apple" + config SND_SOC_APPLE_MCA tristate "Apple Silicon MCA driver" depends on ARCH_APPLE || COMPILE_TEST @@ -5,3 +7,5 @@ config SND_SOC_APPLE_MCA help This option enables an ASoC platform driver for MCA peripherals found on Apple Silicon SoCs. + +endmenu -- GitLab From 4f30f84feb7757f09aeecba120a05e1145b8f264 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:21 +0000 Subject: [PATCH 252/868] ASoC: atmel: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ecvpzqdi.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 5d59e00be8238..4f51612f3dd2b 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -1,13 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_ATMEL_SOC - tristate "SoC Audio for the Atmel System-on-Chip" +menu "Atmel" depends on HAS_IOMEM - help - Say Y or M if you want to add support for codecs attached to - the ATMEL SSC interface. You will also need - to select the audio interfaces to support below. - -if SND_ATMEL_SOC config SND_ATMEL_SOC_PDC bool @@ -176,4 +169,4 @@ config SND_MCHP_SOC_PDMC 2 data lines. The signal path includes an audio grade programmable decimation filter and outputs 24-bit audio words. -endif +endmenu -- GitLab From c153c508e5b41405db3ac9ce32169af6961aa878 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:26 +0000 Subject: [PATCH 253/868] ASoC: intel: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87cyb9zqde.wl-kuninori.morimoto.gx@renesas.com Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 2db494b0e3cff..412555e626b81 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Intel" + config SND_SOC_INTEL_SST_TOPLEVEL bool "Intel ASoC SST drivers" default y @@ -115,3 +117,5 @@ source "sound/soc/intel/avs/boards/Kconfig" # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" + +endmenu -- GitLab From 7798775a033e7d19270e20bcd105c29e373b8bad Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:30 +0000 Subject: [PATCH 254/868] ASoC: spear: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87bjqtzqd9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/spear/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 1b053de1ae7cb..84a52fd4c3ea1 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "ST SPEAr" + config SND_SPEAR_SOC tristate select SND_SOC_GENERIC_DMAENGINE_PCM @@ -8,3 +10,5 @@ config SND_SPEAR_SPDIF_OUT config SND_SPEAR_SPDIF_IN tristate + +endmenu -- GitLab From c0262c187a0b8473169156f4cf3aad0f41b08d00 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:35 +0000 Subject: [PATCH 255/868] ASoC: sunxi: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Acked-by: Chen-Yu Tsai Link: https://patch.msgid.link/87a56dzqd4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/sunxi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index 1f18f016acbb8..205f422d9ded1 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "Allwinner SoC Audio support" +menu "Allwinner" depends on ARCH_SUNXI || COMPILE_TEST config SND_SUN4I_CODEC -- GitLab From 41b94a6f2debeb0483d8cda0558df860c0a26969 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:40 +0000 Subject: [PATCH 256/868] ASoC: tegra: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Reviewed-by: Jon Hunter Link: https://patch.msgid.link/878qlxzqd0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/tegra/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 2463c22e9cf6d..71203a9197ede 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Tegra" + config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST @@ -293,3 +295,5 @@ config SND_SOC_TEGRA_SGTL5000 Colibri T30. endif + +endmenu -- GitLab From 5b8b93e695bb8b5aa0d6432f397e8126faf56f5f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:45 +0000 Subject: [PATCH 257/868] ASoC: ux500: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/877c1hzqcv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ux500/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig index 34b2438b52d36..1130580603500 100644 --- a/sound/soc/ux500/Kconfig +++ b/sound/soc/ux500/Kconfig @@ -3,7 +3,7 @@ # Ux500 SoC audio configuration # menuconfig SND_SOC_UX500 - tristate "SoC Audio support for Ux500 platform" + tristate "Ux500" depends on SND_SOC depends on MFD_DB8500_PRCMU help -- GitLab From 8a40e95a1328b8045094b1037c457358c56700cb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:51 +0000 Subject: [PATCH 258/868] ASoC: cirrus: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Reviewed-by: Alexander Sverdlin Link: https://patch.msgid.link/875xh1zqcp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/cirrus/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 97def4e53fbc7..31475e64e7dd8 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Cirrus Logic" + config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" depends on ARCH_EP93XX || COMPILE_TEST @@ -31,3 +33,4 @@ config SND_EP93XX_SOC_I2S_WATCHDOG endif # if SND_EP93XX_SOC_I2S +endmenu -- GitLab From c6ddacab374702ed187359f103a95ad187d16d94 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:43:55 +0000 Subject: [PATCH 259/868] ASoC: google: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/874iwlzqck.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/google/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig index 7603782fb0602..6005653170a8f 100644 --- a/sound/soc/google/Kconfig +++ b/sound/soc/google/Kconfig @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Google" config SND_SOC_CHV3_I2S tristate "Google Chameleon v3 I2S device" help Enable support for the Google Chameleon v3 I2S device. + +endmenu -- GitLab From 099ae845c6b23ea2865a3912be3f51408f62f334 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:00 +0000 Subject: [PATCH 260/868] ASoC: jz4740: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/8734c5zqcg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/jz4740/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig index dd3b4507fbe6d..f3ff3fb492392 100644 --- a/sound/soc/jz4740/Kconfig +++ b/sound/soc/jz4740/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "JZ4740" + config SND_JZ4740_SOC_I2S tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC" depends on MIPS || COMPILE_TEST @@ -8,3 +10,5 @@ config SND_JZ4740_SOC_I2S help Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740 based boards. + +endmenu -- GitLab From 69fa5909b3dc8b7968192d110e0d7ec8ef8dfd45 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:05 +0000 Subject: [PATCH 261/868] ASoC: xilinx: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/871prpzqca.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/xilinx/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig index 5bd2730aab76f..670e0f3010972 100644 --- a/sound/soc/xilinx/Kconfig +++ b/sound/soc/xilinx/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Xilinx" + config SND_SOC_XILINX_I2S tristate "Audio support for the Xilinx I2S" help @@ -21,3 +23,5 @@ config SND_SOC_XILINX_SPDIF Select this option to enable Xilinx SPDIF Audio. This provides playback and capture of SPDIF audio in AES format. + +endmenu -- GitLab From 41d88bb7546e445806febf45c971b854ba4bc3d7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:10 +0000 Subject: [PATCH 262/868] ASoC: xtensa: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87zfedybrp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/xtensa/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/xtensa/Kconfig b/sound/soc/xtensa/Kconfig index 74b778f186cef..87457272c7731 100644 --- a/sound/soc/xtensa/Kconfig +++ b/sound/soc/xtensa/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Xtensa" + config SND_SOC_XTFPGA_I2S tristate "XTFPGA I2S master" select REGMAP_MMIO @@ -6,3 +8,5 @@ config SND_SOC_XTFPGA_I2S Say Y or M if you want to add support for codecs attached to the I2S interface on XTFPGA daughter board. You will also need to select the drivers for the rest of XTFPGA audio subsystem. + +endmenu -- GitLab From a549459a05c0a46aaf6fdf0d21a42ff09cbac91a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:15 +0000 Subject: [PATCH 263/868] ASoC: renesas: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87y0txybrk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/renesas/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig index dabf02a955ca1..11c2027c88a71 100644 --- a/sound/soc/renesas/Kconfig +++ b/sound/soc/renesas/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -menu "SoC Audio support for Renesas SoCs" +menu "Renesas" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST config SND_SOC_PCM_SH7760 -- GitLab From acc84d15e45393fbbe87758c5fed25d01c83ef92 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:20 +0000 Subject: [PATCH 264/868] ASoC: generic: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87wm9hybrf.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/Kconfig | 4 +--- sound/soc/generic/Kconfig | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index ce74818bd7152..bf362bfca4564 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -111,6 +111,7 @@ source "sound/soc/bcm/Kconfig" source "sound/soc/cirrus/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/generic/Kconfig" source "sound/soc/google/Kconfig" source "sound/soc/hisilicon/Kconfig" source "sound/soc/jz4740/Kconfig" @@ -148,8 +149,5 @@ source "sound/soc/codecs/Kconfig" source "sound/soc/sdw_utils/Kconfig" -# generic frame-work -source "sound/soc/generic/Kconfig" - endif # SND_SOC diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index b6df4e26bc4ad..64b0817e29550 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Generic drivers" + config SND_SIMPLE_CARD_UTILS tristate @@ -37,3 +39,5 @@ config SND_TEST_COMPONENT depends on OF help This option enables test component sound driver support. + +endmenu -- GitLab From 0fa7adb638f6c4e8e458056adbcfc028a8085cf8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:25 +0000 Subject: [PATCH 265/868] ASoC: samsung: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87v7p1ybrb.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 60b4b7b752155..ec7204f57fd43 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SND_SOC_SAMSUNG - tristate "ASoC support for Samsung" + tristate "Samsung" depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM -- GitLab From 9fad9eb0371b11af1cea3e32cb423913cd10582a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:29 +0000 Subject: [PATCH 266/868] ASoC: kirkwood: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87tt4lybr6.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/kirkwood/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 5d8a86b26fa2f..924072e402c86 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Kirkwood" + config SND_KIRKWOOD_SOC tristate "SoC Audio for the Marvell Kirkwood and Dove chips" depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST @@ -16,3 +18,4 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB Say Y if you want to add support for SoC audio on the Armada 370 Development Board. +endmenu -- GitLab From 9c0169922db5cec50a9f470df098dd835477c388 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:35 +0000 Subject: [PATCH 267/868] ASoC: loongson: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87sek5ybr0.wl-kuninori.morimoto.gx@renesas.com Reviewed-by: Binbin Zhou Signed-off-by: Mark Brown --- sound/soc/loongson/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig index 1a3c28816e7ad..2e06670e4d7e8 100644 --- a/sound/soc/loongson/Kconfig +++ b/sound/soc/loongson/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -menu "SoC Audio for Loongson CPUs" +menu "Loongson" config SND_SOC_LOONGSON_CARD tristate "Loongson Sound Card Driver" -- GitLab From 05016f7e481e3d8d9ea539c05a0e8aba2c4f22ee Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:41 +0000 Subject: [PATCH 268/868] ASoC: mediatek: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Reviewed-by: Matthias Brugger Link: https://patch.msgid.link/87qzzpybqu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 90e367586493d..10ca8bccabdd8 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Mediatek" + config SND_SOC_MEDIATEK tristate select REGMAP_MMIO @@ -319,3 +321,5 @@ config SND_SOC_MT8365_MT6357 boards with the MT6357 PMIC codec. Select Y if you have such device. If unsure select "N". + +endmenu -- GitLab From cae3cc435db56f2896be1d114952b4da0ac2eda8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:46 +0000 Subject: [PATCH 269/868] ASoC: rockchip: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87plf9ybqp.wl-kuninori.morimoto.gx@renesas.com Reviewed-by: Heiko Stuebner Signed-off-by: Mark Brown --- sound/soc/rockchip/Kconfig | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index a08544827b2a3..bd210fafe9fea 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -1,15 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_ROCKCHIP - tristate "ASoC support for Rockchip" +menu "Rockchip" depends on COMPILE_TEST || ARCH_ROCKCHIP - help - Say Y or M if you want to add support for codecs attached to - the Rockchip SoCs' Audio interfaces. You will also need to - select the audio interfaces to support below. + depends on HAVE_CLK config SND_SOC_ROCKCHIP_I2S tristate "Rockchip I2S Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for @@ -18,7 +13,6 @@ config SND_SOC_ROCKCHIP_I2S config SND_SOC_ROCKCHIP_I2S_TDM tristate "Rockchip I2S/TDM Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for the I2S/TDM driver for @@ -29,7 +23,6 @@ config SND_SOC_ROCKCHIP_I2S_TDM config SND_SOC_ROCKCHIP_PDM tristate "Rockchip PDM Controller Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM select RATIONAL help @@ -39,7 +32,6 @@ config SND_SOC_ROCKCHIP_PDM config SND_SOC_ROCKCHIP_SAI tristate "Rockchip SAI Controller Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for the Rockchip Serial Audio @@ -49,7 +41,6 @@ config SND_SOC_ROCKCHIP_SAI config SND_SOC_ROCKCHIP_SPDIF tristate "Rockchip SPDIF Device Driver" - depends on HAVE_CLK && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for SPDIF driver for @@ -57,7 +48,7 @@ config SND_SOC_ROCKCHIP_SPDIF config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98090 select SND_SOC_TS3A227E @@ -68,7 +59,7 @@ config SND_SOC_ROCKCHIP_MAX98090 config SND_SOC_ROCKCHIP_RT5645 tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_RT5645 help @@ -77,7 +68,7 @@ config SND_SOC_ROCKCHIP_RT5645 config SND_SOC_RK3288_HDMI_ANALOG tristate "ASoC support multiple codecs for Rockchip RK3288 boards" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK + depends on I2C && GPIOLIB select SND_SOC_ROCKCHIP_I2S select SND_SOC_HDMI_CODEC select SND_SOC_ES8328_I2C @@ -89,7 +80,7 @@ config SND_SOC_RK3288_HDMI_ANALOG config SND_SOC_RK3399_GRU_SOUND tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards" - depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK && SPI + depends on I2C && GPIOLIB && SPI select SND_SOC_ROCKCHIP_I2S select SND_SOC_MAX98357A select SND_SOC_RT5514 @@ -100,3 +91,5 @@ config SND_SOC_RK3399_GRU_SOUND help Say Y or M here if you want to add support multiple codecs for SoC audio on Rockchip RK3399 GRU boards. + +endmenu -- GitLab From acc317e5254a739eedf60b53aaf31f69f55e3dc8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:51 +0000 Subject: [PATCH 270/868] ASoC: starfive: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6utybqk.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/starfive/Kconfig | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig index 279ac5c1d309b..1e11aa74594d7 100644 --- a/sound/soc/starfive/Kconfig +++ b/sound/soc/starfive/Kconfig @@ -1,15 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_STARFIVE - tristate "Audio support for StarFive SoC" +menu "StarFive" depends on COMPILE_TEST || ARCH_STARFIVE - help - Say Y or M if you want to add support for codecs attached to - the Starfive SoCs' Audio interfaces. You will also need to - select the audio interfaces to support below. + depends on HAVE_CLK config SND_SOC_JH7110_PWMDAC tristate "JH7110 PWM-DAC device driver" - depends on HAVE_CLK && SND_SOC_STARFIVE select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_SPDIF help @@ -18,7 +13,8 @@ config SND_SOC_JH7110_PWMDAC config SND_SOC_JH7110_TDM tristate "JH7110 TDM device driver" - depends on HAVE_CLK && SND_SOC_STARFIVE select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for StarFive TDM driver. + +endmenu -- GitLab From ac131c4148bcfa682104628d2174e8e38e03fd25 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:44:57 +0000 Subject: [PATCH 271/868] ASoC: uniphier: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Reviewed-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/87msadybqf.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/uniphier/Kconfig | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig index ddfa6424c656b..7c978639e0aac 100644 --- a/sound/soc/uniphier/Kconfig +++ b/sound/soc/uniphier/Kconfig @@ -1,18 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 -config SND_SOC_UNIPHIER - tristate "ASoC support for UniPhier" +menu "UniPhier" depends on (ARCH_UNIPHIER || COMPILE_TEST) - help - Say Y or M if you want to add support for the Socionext - UniPhier SoC audio interfaces. You will also need to select the - audio interfaces to support below. - If unsure select "N". config SND_SOC_UNIPHIER_AIO tristate "UniPhier AIO DAI Driver" select REGMAP_MMIO select SND_SOC_COMPRESS - depends on SND_SOC_UNIPHIER help This adds ASoC driver support for Socionext UniPhier 'AIO' Audio Input/Output subsystem. @@ -21,7 +14,6 @@ config SND_SOC_UNIPHIER_AIO config SND_SOC_UNIPHIER_LD11 tristate "UniPhier LD11/LD20 Device Driver" - depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO help This adds ASoC driver for Socionext UniPhier LD11/LD20 @@ -31,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11 config SND_SOC_UNIPHIER_PXS2 tristate "UniPhier PXs2 Device Driver" - depends on SND_SOC_UNIPHIER select SND_SOC_UNIPHIER_AIO help This adds ASoC driver for Socionext UniPhier PXs2 @@ -41,10 +32,11 @@ config SND_SOC_UNIPHIER_PXS2 config SND_SOC_UNIPHIER_EVEA_CODEC tristate "UniPhier SoC internal audio codec" - depends on SND_SOC_UNIPHIER select REGMAP_MMIO help This adds Codec driver for Socionext UniPhier LD11/20 SoC internal DAC. This driver supports Line In / Out and HeadPhone. Select Y if you use such device. If unsure select "N". + +endmenu -- GitLab From 68fbc70ece40139380380dce74059afa592846b3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 12 Jun 2025 01:45:03 +0000 Subject: [PATCH 272/868] ASoC: hisilicon: Standardize ASoC menu Current Kconfig menu at [ALSA for SoC audio support] has no rules. So, some venders are using menu style, some venders are listed each drivers on top page, etc. It is difficult to find target vender and/or drivers because it is very random. Let's standardize ASoC menu Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ldpxybq9.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/hisilicon/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig index df8fbd8bb4b03..d95da932f3524 100644 --- a/sound/soc/hisilicon/Kconfig +++ b/sound/soc/hisilicon/Kconfig @@ -1,6 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "Hisilicon" + config SND_I2S_HI6210_I2S tristate "Hisilicon I2S controller" select SND_SOC_GENERIC_DMAENGINE_PCM help Hisilicon I2S + +endmenu -- GitLab From 981d7f91aeda17424b29f033249f4fa7cd2a7556 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Mon, 23 Jun 2025 14:05:18 -0700 Subject: [PATCH 273/868] platform/chrome: cros_ec_sensorhub: Retries when a sensor is not ready When the EC/ISH starts, it can take a while for all the sensors to be up and running or declared broken. If the sensor stack return -EBUSY when checking for sensor information, retry up to 50 times. It has been observed 100ms wait time is enough to have valid sensors ready. It can take more time in case a sensor is really broken and is not coming up. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20250623210518.306740-1-gwendal@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_sensorhub.c | 23 +++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c index 50cdae67fa320..9bad8f72680ea 100644 --- a/drivers/platform/chrome/cros_ec_sensorhub.c +++ b/drivers/platform/chrome/cros_ec_sensorhub.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #define DRV_NAME "cros-ec-sensorhub" +#define CROS_EC_CMD_INFO_RETRIES 50 static void cros_ec_sensorhub_free_sensor(void *arg) { @@ -53,7 +55,7 @@ static int cros_ec_sensorhub_register(struct device *dev, int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 }; struct cros_ec_command *msg = sensorhub->msg; struct cros_ec_dev *ec = sensorhub->ec; - int ret, i; + int ret, i, retries; char *name; @@ -65,12 +67,25 @@ static int cros_ec_sensorhub_register(struct device *dev, sensorhub->params->cmd = MOTIONSENSE_CMD_INFO; sensorhub->params->info.sensor_num = i; - ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + retries = CROS_EC_CMD_INFO_RETRIES; + do { + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret == -EBUSY) { + /* The EC is still busy initializing sensors. */ + usleep_range(5000, 6000); + retries--; + } + } while (ret == -EBUSY && retries); + if (ret < 0) { - dev_warn(dev, "no info for EC sensor %d : %d/%d\n", - i, ret, msg->result); + dev_err(dev, "no info for EC sensor %d : %d/%d\n", + i, ret, msg->result); continue; } + if (retries < CROS_EC_CMD_INFO_RETRIES) { + dev_warn(dev, "%d retries needed to bring up sensor %d\n", + CROS_EC_CMD_INFO_RETRIES - retries, i); + } switch (sensorhub->resp->info.type) { case MOTIONSENSE_TYPE_ACCEL: -- GitLab From 3e36c822506d924894ff7de549b9377d3114c2d7 Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Tue, 24 Jun 2025 09:00:28 +0530 Subject: [PATCH 274/868] spi: spi-pci1xxxx: Add support for per-instance DMA interrupt vectors Add support for dedicated DMA interrupt vectors for each SPI hardware instance in the pci1xxxx driver. This improves scalability and interrupt handling for systems using multiple SPI instances with DMA. Introduce a constant `NUM_VEC_PER_INST` to define the number of IRQ vectors per instance (main, DMA write, DMA read). Update the `pci1xxxx_spi_internal` structure to use an IRQ array. Refactor IRQ allocation and DMA initialization logic: - Assign separate IRQ vectors for DMA read and write interrupts. - Split the original DMA ISR into two handlers: `pci1xxxx_spi_isr_dma_rd` and `pci1xxxx_spi_isr_dma_wr`. - Configure IMWR registers per instance using cached MSI data. - Move DMA register configuration into a new helper function, `pci1xxxx_spi_dma_config()`. Invoke the DMA initialization after all instances are configured to ensure correct IRQ vector mapping. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250624033028.74389-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 212 +++++++++++++++++++++++++------------ 1 file changed, 144 insertions(+), 68 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index a6c8bf228288f..f44fe58411392 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -97,8 +97,8 @@ #define SPI_DMA_CH1_DONE_INT BIT(1) #define SPI_DMA_CH0_ABORT_INT BIT(16) #define SPI_DMA_CH1_ABORT_INT BIT(17) -#define SPI_DMA_DONE_INT_MASK (SPI_DMA_CH0_DONE_INT | SPI_DMA_CH1_DONE_INT) -#define SPI_DMA_ABORT_INT_MASK (SPI_DMA_CH0_ABORT_INT | SPI_DMA_CH1_ABORT_INT) +#define SPI_DMA_DONE_INT_MASK(x) (1 << (x)) +#define SPI_DMA_ABORT_INT_MASK(x) (1 << (16 + (x))) #define DMA_CH_CONTROL_LIE BIT(3) #define DMA_CH_CONTROL_RIE BIT(4) #define DMA_INTR_EN (DMA_CH_CONTROL_RIE | DMA_CH_CONTROL_LIE) @@ -132,10 +132,12 @@ #define SPI_SUSPEND_CONFIG 0x101 #define SPI_RESUME_CONFIG 0x203 +#define NUM_VEC_PER_INST 3 + struct pci1xxxx_spi_internal { u8 hw_inst; u8 clkdiv; - int irq; + int irq[NUM_VEC_PER_INST]; int mode; bool spi_xfer_in_progress; void *rx_buf; @@ -193,6 +195,9 @@ static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table); +static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev); +static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev); + static int pci1xxxx_set_sys_lock(struct pci1xxxx_spi *par) { writel(SPI_SYSLOCK, par->reg_base + SPI_SYSLOCK_REG); @@ -213,13 +218,16 @@ static void pci1xxxx_release_sys_lock(struct pci1xxxx_spi *par) writel(0x0, par->reg_base + SPI_SYSLOCK_REG); } -static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) +static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector) { struct pci_dev *pdev = spi_bus->dev; u32 pf_num; u32 regval; int ret; + if (num_vector != hw_inst * NUM_VEC_PER_INST) + return -EOPNOTSUPP; + /* * DEV REV Registers is a system register, HW Syslock bit * should be acquired before accessing the register @@ -247,16 +255,6 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) if (spi_bus->dev_rev < 0xC0 || pf_num) return -EOPNOTSUPP; - /* - * DMA Supported only with MSI Interrupts - * One of the SPI instance's MSI vector address and data - * is used for DMA Interrupt - */ - if (!irq_get_msi_desc(irq)) { - dev_warn(&pdev->dev, "Error MSI Interrupt not supported, will operate in PIO mode\n"); - return -EOPNOTSUPP; - } - spi_bus->dma_offset_bar = pcim_iomap(pdev, 2, pci_resource_len(pdev, 2)); if (!spi_bus->dma_offset_bar) { dev_warn(&pdev->dev, "Error failed to map dma bar, will operate in PIO mode\n"); @@ -273,29 +271,90 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq) return 0; } -static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int irq) +static void pci1xxxx_spi_dma_config(struct pci1xxxx_spi *spi_bus) { + struct pci1xxxx_spi_internal *spi_sub_ptr; + u8 iter, irq_index; struct msi_msg msi; + u32 regval; + u16 data; + + irq_index = spi_bus->total_hw_instances; + for (iter = 0; iter < spi_bus->total_hw_instances; iter++) { + spi_sub_ptr = spi_bus->spi_int[iter]; + get_cached_msi_msg(spi_sub_ptr->irq[1], &msi); + if (iter == 0) { + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WDONE_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WABORT_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RDONE_HIGH); + writel(msi.address_hi, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RABORT_HIGH); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WDONE_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_WABORT_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RDONE_LOW); + writel(msi.address_lo, spi_bus->dma_offset_bar + + SPI_DMA_INTR_IMWR_RABORT_LOW); + writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + } + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + data = msi.data + irq_index; + writel((regval | (data << (iter * 16))), spi_bus->dma_offset_bar + + SPI_DMA_INTR_WR_IMWR_DATA); + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); + irq_index++; + + data = msi.data + irq_index; + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + writel(regval | (data << (iter * 16)), spi_bus->dma_offset_bar + + SPI_DMA_INTR_RD_IMWR_DATA); + regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + irq_index++; + } +} + +static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector) +{ + struct pci1xxxx_spi_internal *spi_sub_ptr; + u8 iter, irq_index; int ret; - ret = pci1xxxx_check_spi_can_dma(spi_bus, irq); + irq_index = hw_inst; + ret = pci1xxxx_check_spi_can_dma(spi_bus, hw_inst, num_vector); if (ret) return ret; spin_lock_init(&spi_bus->dma_reg_lock); - get_cached_msi_msg(irq, &msi); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_HIGH); - writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_HIGH); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_LOW); - writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_LOW); - writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA); - writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA); + + for (iter = 0; iter < hw_inst; iter++) { + spi_sub_ptr = spi_bus->spi_int[iter]; + spi_sub_ptr->irq[1] = pci_irq_vector(spi_bus->dev, irq_index); + ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[1], + pci1xxxx_spi_isr_dma_wr, PCI1XXXX_IRQ_FLAGS, + pci_name(spi_bus->dev), spi_sub_ptr); + if (ret < 0) + return ret; + + irq_index++; + + spi_sub_ptr->irq[2] = pci_irq_vector(spi_bus->dev, irq_index); + ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[2], + pci1xxxx_spi_isr_dma_rd, PCI1XXXX_IRQ_FLAGS, + pci_name(spi_bus->dev), spi_sub_ptr); + if (ret < 0) + return ret; + + irq_index++; + } + pci1xxxx_spi_dma_config(spi_bus); dma_set_max_seg_size(&spi_bus->dev->dev, PCI1XXXX_SPI_BUFFER_SIZE); spi_bus->can_dma = true; return 0; @@ -401,13 +460,13 @@ static void pci1xxxx_spi_setup(struct pci1xxxx_spi *par, u8 hw_inst, u32 mode, writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); } -static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p, u8 hw_inst) +static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p) { u32 regval; - regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); + regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); regval |= SPI_MST_CTL_GO; - writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst)); + writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); } static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, @@ -451,7 +510,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr, &tx_buf[bytes_transfered], len); bytes_transfered += len; pci1xxxx_spi_setup(par, p->hw_inst, spi->mode, clkdiv, len); - pci1xxxx_start_spi_xfer(p, p->hw_inst); + pci1xxxx_start_spi_xfer(p); /* Wait for DMA_TERM interrupt */ result = wait_for_completion_timeout(&p->spi_xfer_done, @@ -627,7 +686,7 @@ static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p } } -static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) +static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev) { struct pci1xxxx_spi_internal *p = dev; irqreturn_t spi_int_fired = IRQ_NONE; @@ -637,36 +696,53 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA RD INT and start spi xfer*/ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS); - if (regval & SPI_DMA_DONE_INT_MASK) { - if (regval & SPI_DMA_CH0_DONE_INT) - pci1xxxx_start_spi_xfer(p, SPI0); - if (regval & SPI_DMA_CH1_DONE_INT) - pci1xxxx_start_spi_xfer(p, SPI1); - spi_int_fired = IRQ_HANDLED; - } - if (regval & SPI_DMA_ABORT_INT_MASK) { - p->dma_aborted_rd = true; - spi_int_fired = IRQ_HANDLED; + if (regval) { + if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { + pci1xxxx_start_spi_xfer(p); + spi_int_fired = IRQ_HANDLED; + } + if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { + p->dma_aborted_rd = true; + spi_int_fired = IRQ_HANDLED; + } } - writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); + return spi_int_fired; +} +static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev) +{ + struct pci1xxxx_spi_internal *p = dev; + irqreturn_t spi_int_fired = IRQ_NONE; + unsigned long flags; + u32 regval; + + spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA WR INT */ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS); - if (regval & SPI_DMA_DONE_INT_MASK) { - if (regval & SPI_DMA_CH0_DONE_INT) - pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI0]); - - if (regval & SPI_DMA_CH1_DONE_INT) - pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI1]); - - spi_int_fired = IRQ_HANDLED; - } - if (regval & SPI_DMA_ABORT_INT_MASK) { - p->dma_aborted_wr = true; - spi_int_fired = IRQ_HANDLED; + if (regval) { + if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { + pci1xxxx_spi_setup_next_dma_transfer(p); + spi_int_fired = IRQ_HANDLED; + } + if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { + p->dma_aborted_wr = true; + spi_int_fired = IRQ_HANDLED; + } } - writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); + return spi_int_fired; +} + +static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) +{ + struct pci1xxxx_spi_internal *p = dev; + irqreturn_t spi_int_fired = IRQ_NONE; + u32 regval; /* Clear the SPI GO_BIT Interrupt */ regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); @@ -764,7 +840,7 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (!spi_bus->reg_base) return -EINVAL; - num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt, + num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt * NUM_VEC_PER_INST, PCI_IRQ_INTX | PCI_IRQ_MSI); if (num_vector < 0) { dev_err(&pdev->dev, "Error allocating MSI vectors\n"); @@ -778,27 +854,23 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * regval &= ~SPI_INTR; writel(regval, spi_bus->reg_base + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); - spi_sub_ptr->irq = pci_irq_vector(pdev, 0); + spi_sub_ptr->irq[0] = pci_irq_vector(pdev, 0); if (num_vector >= hw_inst_cnt) - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, pci_name(pdev), spi_sub_ptr); else - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_shared_isr, PCI1XXXX_IRQ_FLAGS | IRQF_SHARED, pci_name(pdev), spi_bus); if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", - spi_sub_ptr->irq); + spi_sub_ptr->irq[0]); return -ENODEV; } - ret = pci1xxxx_spi_dma_init(spi_bus, spi_sub_ptr->irq); - if (ret && ret != -EOPNOTSUPP) - return ret; - /* This register is only applicable for 1st instance */ regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0)); if (!only_sec_inst) @@ -820,13 +892,13 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * writel(regval, spi_bus->reg_base + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); if (num_vector >= hw_inst_cnt) { - spi_sub_ptr->irq = pci_irq_vector(pdev, iter); - ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + spi_sub_ptr->irq[0] = pci_irq_vector(pdev, iter); + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0], pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, pci_name(pdev), spi_sub_ptr); if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", - spi_sub_ptr->irq); + spi_sub_ptr->irq[0]); return -ENODEV; } } @@ -849,6 +921,10 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (ret) return ret; } + ret = pci1xxxx_spi_dma_init(spi_bus, hw_inst_cnt, num_vector); + if (ret && ret != -EOPNOTSUPP) + return ret; + pci_set_drvdata(pdev, spi_bus); return 0; -- GitLab From d4c2d9b5b7ceed14a3a835fd969bb0699b9608d3 Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Mon, 23 Jun 2025 13:42:39 +0200 Subject: [PATCH 275/868] power: sequencing: Add T-HEAD TH1520 GPU power sequencer driver Introduce the pwrseq-thead-gpu driver, a power sequencer provider for the Imagination BXM-4-64 GPU on the T-HEAD TH1520 SoC. This driver controls an auxiliary device instantiated by the AON power domain. The TH1520 GPU requires a specific sequence to correctly initialize and power down its resources: - Enable GPU clocks (core and sys). - De-assert the GPU clock generator reset (clkgen_reset). - Introduce a short hardware-required delay. - De-assert the GPU core reset. The power-down sequence performs these steps in reverse. Implement this sequence via the pwrseq_power_on and pwrseq_power_off callbacks. Crucially, the driver's match function is called when a consumer (the Imagination GPU driver) requests the "gpu-power" target. During this match, the sequencer uses clk_bulk_get() and reset_control_get_exclusive() on the consumer's device to obtain handles to the GPU's "core" and "sys" clocks, and the GPU core reset. These, along with clkgen_reset obtained from parent aon node, allow it to perform the complete sequence. Reviewed-by: Ulf Hansson Signed-off-by: Michal Wilczynski Link: https://lore.kernel.org/r/20250623-apr_14_for_sending-v6-1-6583ce0f6c25@samsung.com [Bartosz: use a ternary operator instead of implicitly casting the result of a boolean expression to int] Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 + drivers/power/sequencing/Kconfig | 8 + drivers/power/sequencing/Makefile | 1 + drivers/power/sequencing/pwrseq-thead-gpu.c | 247 ++++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 drivers/power/sequencing/pwrseq-thead-gpu.c diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163..a7a5f95fb8a48 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21393,6 +21393,7 @@ F: drivers/mailbox/mailbox-th1520.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c F: drivers/pinctrl/pinctrl-th1520.c F: drivers/pmdomain/thead/ +F: drivers/power/sequencing/pwrseq-thead-gpu.c F: drivers/reset/reset-th1520.c F: include/dt-bindings/clock/thead,th1520-clk-ap.h F: include/dt-bindings/power/thead,th1520-power.h diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig index ddcc42a984921..0f118d57c1ced 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -27,4 +27,12 @@ config POWER_SEQUENCING_QCOM_WCN this driver is needed for correct power control or else we'd risk not respecting the required delays between enabling Bluetooth and WLAN. +config POWER_SEQUENCING_TH1520_GPU + tristate "T-HEAD TH1520 GPU power sequencing driver" + depends on ARCH_THEAD && AUXILIARY_BUS + help + Say Y here to enable the power sequencing driver for the TH1520 SoC + GPU. This driver handles the complex clock and reset sequence + required to power on the Imagination BXM GPU on this platform. + endif diff --git a/drivers/power/sequencing/Makefile b/drivers/power/sequencing/Makefile index 2eec2df7912d1..96c1cf0a98ac5 100644 --- a/drivers/power/sequencing/Makefile +++ b/drivers/power/sequencing/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_POWER_SEQUENCING) += pwrseq-core.o pwrseq-core-y := core.o obj-$(CONFIG_POWER_SEQUENCING_QCOM_WCN) += pwrseq-qcom-wcn.o +obj-$(CONFIG_POWER_SEQUENCING_TH1520_GPU) += pwrseq-thead-gpu.o diff --git a/drivers/power/sequencing/pwrseq-thead-gpu.c b/drivers/power/sequencing/pwrseq-thead-gpu.c new file mode 100644 index 0000000000000..3dd27c32020a6 --- /dev/null +++ b/drivers/power/sequencing/pwrseq-thead-gpu.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * T-HEAD TH1520 GPU Power Sequencer Driver + * + * Copyright (c) 2025 Samsung Electronics Co., Ltd. + * Author: Michal Wilczynski + * + * This driver implements the power sequence for the Imagination BXM-4-64 + * GPU on the T-HEAD TH1520 SoC. The sequence requires coordinating resources + * from both the sequencer's parent device node (clkgen_reset) and the GPU's + * device node (clocks and core reset). + * + * The `match` function is used to acquire the GPU's resources when the + * GPU driver requests the "gpu-power" sequence target. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct pwrseq_thead_gpu_ctx { + struct pwrseq_device *pwrseq; + struct reset_control *clkgen_reset; + struct device_node *aon_node; + + /* Consumer resources */ + struct device_node *consumer_node; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *gpu_reset; +}; + +static int pwrseq_thead_gpu_enable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + int ret; + + if (!ctx->clks || !ctx->gpu_reset) + return -ENODEV; + + ret = clk_bulk_prepare_enable(ctx->num_clks, ctx->clks); + if (ret) + return ret; + + ret = reset_control_deassert(ctx->clkgen_reset); + if (ret) + goto err_disable_clks; + + /* + * According to the hardware manual, a delay of at least 32 clock + * cycles is required between de-asserting the clkgen reset and + * de-asserting the GPU reset. Assuming a worst-case scenario with + * a very high GPU clock frequency, a delay of 1 microsecond is + * sufficient to ensure this requirement is met across all + * feasible GPU clock speeds. + */ + udelay(1); + + ret = reset_control_deassert(ctx->gpu_reset); + if (ret) + goto err_assert_clkgen; + + return 0; + +err_assert_clkgen: + reset_control_assert(ctx->clkgen_reset); +err_disable_clks: + clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks); + return ret; +} + +static int pwrseq_thead_gpu_disable(struct pwrseq_device *pwrseq) +{ + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + int ret = 0, err; + + if (!ctx->clks || !ctx->gpu_reset) + return -ENODEV; + + err = reset_control_assert(ctx->gpu_reset); + if (err) + ret = err; + + err = reset_control_assert(ctx->clkgen_reset); + if (err && !ret) + ret = err; + + clk_bulk_disable_unprepare(ctx->num_clks, ctx->clks); + + /* ret stores values of the first error code */ + return ret; +} + +static const struct pwrseq_unit_data pwrseq_thead_gpu_unit = { + .name = "gpu-power-sequence", + .enable = pwrseq_thead_gpu_enable, + .disable = pwrseq_thead_gpu_disable, +}; + +static const struct pwrseq_target_data pwrseq_thead_gpu_target = { + .name = "gpu-power", + .unit = &pwrseq_thead_gpu_unit, +}; + +static const struct pwrseq_target_data *pwrseq_thead_gpu_targets[] = { + &pwrseq_thead_gpu_target, + NULL +}; + +static int pwrseq_thead_gpu_match(struct pwrseq_device *pwrseq, + struct device *dev) +{ + struct pwrseq_thead_gpu_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); + static const char *const clk_names[] = { "core", "sys" }; + struct of_phandle_args pwr_spec; + int i, ret; + + /* We only match the specific T-HEAD TH1520 GPU compatible */ + if (!of_device_is_compatible(dev->of_node, "thead,th1520-gpu")) + return 0; + + ret = of_parse_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells", 0, &pwr_spec); + if (ret) + return 0; + + /* Additionally verify consumer device has AON as power-domain */ + if (pwr_spec.np != ctx->aon_node || pwr_spec.args[0] != TH1520_GPU_PD) { + of_node_put(pwr_spec.np); + return 0; + } + + of_node_put(pwr_spec.np); + + /* If a consumer is already bound, only allow a re-match from it */ + if (ctx->consumer_node) + return ctx->consumer_node == dev->of_node ? 1 : 0; + + ctx->num_clks = ARRAY_SIZE(clk_names); + ctx->clks = kcalloc(ctx->num_clks, sizeof(*ctx->clks), GFP_KERNEL); + if (!ctx->clks) + return -ENOMEM; + + for (i = 0; i < ctx->num_clks; i++) + ctx->clks[i].id = clk_names[i]; + + ret = clk_bulk_get(dev, ctx->num_clks, ctx->clks); + if (ret) + goto err_free_clks; + + ctx->gpu_reset = reset_control_get_shared(dev, NULL); + if (IS_ERR(ctx->gpu_reset)) { + ret = PTR_ERR(ctx->gpu_reset); + goto err_put_clks; + } + + ctx->consumer_node = of_node_get(dev->of_node); + + return 1; + +err_put_clks: + clk_bulk_put(ctx->num_clks, ctx->clks); +err_free_clks: + kfree(ctx->clks); + ctx->clks = NULL; + + return ret; +} + +static int pwrseq_thead_gpu_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct device *parent_dev = dev->parent; + struct pwrseq_thead_gpu_ctx *ctx; + struct pwrseq_config config = {}; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->aon_node = parent_dev->of_node; + + ctx->clkgen_reset = + devm_reset_control_get_exclusive(parent_dev, "gpu-clkgen"); + if (IS_ERR(ctx->clkgen_reset)) + return dev_err_probe( + dev, PTR_ERR(ctx->clkgen_reset), + "Failed to get GPU clkgen reset from parent\n"); + + config.parent = dev; + config.owner = THIS_MODULE; + config.drvdata = ctx; + config.match = pwrseq_thead_gpu_match; + config.targets = pwrseq_thead_gpu_targets; + + ctx->pwrseq = devm_pwrseq_device_register(dev, &config); + if (IS_ERR(ctx->pwrseq)) + return dev_err_probe(dev, PTR_ERR(ctx->pwrseq), + "Failed to register power sequencer\n"); + + auxiliary_set_drvdata(adev, ctx); + + return 0; +} + +static void pwrseq_thead_gpu_remove(struct auxiliary_device *adev) +{ + struct pwrseq_thead_gpu_ctx *ctx = auxiliary_get_drvdata(adev); + + if (ctx->gpu_reset) + reset_control_put(ctx->gpu_reset); + + if (ctx->clks) { + clk_bulk_put(ctx->num_clks, ctx->clks); + kfree(ctx->clks); + } + + if (ctx->consumer_node) + of_node_put(ctx->consumer_node); +} + +static const struct auxiliary_device_id pwrseq_thead_gpu_id_table[] = { + { .name = "th1520_pm_domains.pwrseq-gpu" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, pwrseq_thead_gpu_id_table); + +static struct auxiliary_driver pwrseq_thead_gpu_driver = { + .driver = { + .name = "pwrseq-thead-gpu", + }, + .probe = pwrseq_thead_gpu_probe, + .remove = pwrseq_thead_gpu_remove, + .id_table = pwrseq_thead_gpu_id_table, +}; +module_auxiliary_driver(pwrseq_thead_gpu_driver); + +MODULE_AUTHOR("Michal Wilczynski "); +MODULE_DESCRIPTION("T-HEAD TH1520 GPU power sequencer driver"); +MODULE_LICENSE("GPL"); -- GitLab From e4feefa5c71912ebfcb97a3dbe2b021fd1cea9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:02 +0200 Subject: [PATCH 276/868] spi: stm32: Add SPI_READY mode to spi controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spi ready functionality is supported by our peripheral in host and target modes on STM32MP2x SoCs. Update our spi controller driver to allow devices to use this functionality. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-1-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index da3517d7102dc..2bcd4a43676da 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -154,6 +154,9 @@ /* STM32H7_SPI_I2SCFGR bit fields */ #define STM32H7_SPI_I2SCFGR_I2SMOD BIT(0) +/* STM32MP25_SPICFG2 bit fields */ +#define STM32MP25_SPI_CFG2_RDIOM BIT(13) + /* STM32MP25 SPI registers bit fields */ #define STM32MP25_SPI_HWCFGR1 0x3F0 @@ -222,6 +225,7 @@ struct stm32_spi_reg { * @rx: SPI RX data register * @tx: SPI TX data register * @fullcfg: SPI full or limited feature set register + * @rdy_en: SPI ready feature register */ struct stm32_spi_regspec { const struct stm32_spi_reg en; @@ -235,6 +239,7 @@ struct stm32_spi_regspec { const struct stm32_spi_reg rx; const struct stm32_spi_reg tx; const struct stm32_spi_reg fullcfg; + const struct stm32_spi_reg rdy_en; }; struct stm32_spi; @@ -415,6 +420,8 @@ static const struct stm32_spi_regspec stm32mp25_spi_regspec = { .tx = { STM32H7_SPI_TXDR }, .fullcfg = { STM32MP25_SPI_HWCFGR1, STM32MP25_SPI_HWCFGR1_FULLCFG }, + + .rdy_en = { STM32H7_SPI_CFG2, STM32MP25_SPI_CFG2_RDIOM }, }; static inline void stm32_spi_set_bits(struct stm32_spi *spi, @@ -1172,15 +1179,21 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl, else clrb |= spi->cfg->regs->cs_high.mask; - dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d\n", + if (spi_dev->mode & SPI_READY) + setb |= spi->cfg->regs->rdy_en.mask; + else + clrb |= spi->cfg->regs->rdy_en.mask; + + dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d rdy=%d\n", !!(spi_dev->mode & SPI_CPOL), !!(spi_dev->mode & SPI_CPHA), !!(spi_dev->mode & SPI_LSB_FIRST), - !!(spi_dev->mode & SPI_CS_HIGH)); + !!(spi_dev->mode & SPI_CS_HIGH), + !!(spi_dev->mode & SPI_READY)); spin_lock_irqsave(&spi->lock, flags); - /* CPOL, CPHA and LSB FIRST bits have common register */ + /* CPOL, CPHA, LSB FIRST, CS_HIGH and RDY_EN bits have common register */ if (clrb || setb) writel_relaxed( (readl_relaxed(spi->base + spi->cfg->regs->cpol.reg) & @@ -1768,6 +1781,13 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, spi->cur_bpw = transfer->bits_per_word; spi->cfg->set_bpw(spi); + if (spi_dev->mode & SPI_READY && spi->cur_bpw < 8) { + writel_relaxed(readl_relaxed(spi->base + spi->cfg->regs->rdy_en.reg) & + ~spi->cfg->regs->rdy_en.mask, + spi->base + spi->cfg->regs->rdy_en.reg); + dev_dbg(spi->dev, "RDY logic disabled as bits per word < 8\n"); + } + /* Update spi->cur_speed with real clock speed */ if (STM32_SPI_HOST_MODE(spi)) { mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz, @@ -2179,7 +2199,7 @@ static int stm32_spi_probe(struct platform_device *pdev) ctrl->auto_runtime_pm = true; ctrl->bus_num = pdev->id; ctrl->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST | - SPI_3WIRE; + SPI_3WIRE | SPI_READY; ctrl->bits_per_word_mask = spi->cfg->get_bpw_mask(spi); ctrl->max_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_min; ctrl->min_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_max; -- GitLab From 21f1c800f6620e43f31dfd76709dbac8ebaa5a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:03 +0200 Subject: [PATCH 277/868] spi: stm32: Check for cfg availability in stm32_spi_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stm32_spi_probe function now includes a check to ensure that the pointer returned by of_device_get_match_data is not NULL before accessing its members. This resolves a warning where a potential NULL pointer dereference could occur when accessing cfg->has_device_mode. Before accessing the 'has_device_mode' member, we verify that 'cfg' is not NULL. If 'cfg' is NULL, an error message is logged. This change ensures that the driver does not attempt to access configuration data if it is not available, thus preventing a potential system crash due to a NULL pointer dereference. Signed-off-by: Clément Le Goffic Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202310191831.MLwx1c6x-lkp@intel.com/ Fixes: fee681646fc8 ("spi: stm32: disable device mode with st,stm32f4-spi compatible") Link: https://patch.msgid.link/20250616-spi-upstream-v1-2-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 2bcd4a43676da..8b61caf770a29 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2089,9 +2089,15 @@ static int stm32_spi_probe(struct platform_device *pdev) struct resource *res; struct reset_control *rst; struct device_node *np = pdev->dev.of_node; + const struct stm32_spi_cfg *cfg; bool device_mode; int ret; - const struct stm32_spi_cfg *cfg = of_device_get_match_data(&pdev->dev); + + cfg = of_device_get_match_data(&pdev->dev); + if (!cfg) { + dev_err(&pdev->dev, "Failed to get match data for platform\n"); + return -ENODEV; + } device_mode = of_property_read_bool(np, "spi-slave"); if (!cfg->has_device_mode && device_mode) { -- GitLab From d17dd2f1d8a1d919e39c6302b024f135a2f90773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:05 +0200 Subject: [PATCH 278/868] spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The STM32 DMA doesn't have the ability to generate convenient burst transfer on the DDR, ensuring the best load of the AXI & DDR. To avoid this bad load of the AXI & DDR, STM32 MDMA can be used to transfer data to the DDR, being triggered by STM32 DMA channel transfer completion. An SRAM buffer is used between DMA and MDMA. So the MDMA always does MEM_TO_MEM transfers (from/to SRAM to/from DDR), and the DMA uses SRAM instead of DDR with DEV_TO_MEM transfers. SPI RX DMA (DEV_TO_MEM) becomes: SPI RX FIFO ==DMA==> SRAM ==MDMA==> DDR In RX (DEV_TO_MEM), EOT interrupt is used to pause the DMA channel (which will raise a transfer complete) to trigger the MDMA to flush the SRAM (when transfer length is not aligned on SRAM period). TX remains on the former implementation. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-4-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 253 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 229 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 8b61caf770a29..8581f24c111f7 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -328,6 +330,11 @@ struct stm32_spi_cfg { * @dma_rx: dma channel for RX transfer * @phys_addr: SPI registers physical base address * @device_mode: the controller is configured as SPI device + * @sram_pool: SRAM pool for DMA transfers + * @sram_rx_buf_size: size of SRAM buffer for RX transfer + * @sram_rx_buf: SRAM buffer for RX transfer + * @sram_dma_rx_buf: SRAM buffer physical address for RX transfer + * @mdma_rx: MDMA channel for RX transfer */ struct stm32_spi { struct device *dev; @@ -362,6 +369,12 @@ struct stm32_spi { dma_addr_t phys_addr; bool device_mode; + + struct gen_pool *sram_pool; + size_t sram_rx_buf_size; + void *sram_rx_buf; + dma_addr_t sram_dma_rx_buf; + struct dma_chan *mdma_rx; }; static const struct stm32_spi_regspec stm32fx_spi_regspec = { @@ -885,8 +898,11 @@ static void stm32h7_spi_disable(struct stm32_spi *spi) if (spi->cur_usedma && spi->dma_tx) dmaengine_terminate_async(spi->dma_tx); - if (spi->cur_usedma && spi->dma_rx) + if (spi->cur_usedma && spi->dma_rx) { dmaengine_terminate_async(spi->dma_rx); + if (spi->mdma_rx) + dmaengine_terminate_async(spi->mdma_rx); + } stm32_spi_clr_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_SPE); @@ -1098,10 +1114,13 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) } if (sr & STM32H7_SPI_SR_EOT) { + dev_dbg(spi->dev, "End of transfer\n"); if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi); if (!spi->cur_usedma || - (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)) + (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) || + (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_FULL_DUPLEX))) end = true; } @@ -1118,6 +1137,11 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) spin_unlock_irqrestore(&spi->lock, flags); if (end) { + if (spi->cur_usedma && spi->mdma_rx) { + dmaengine_pause(spi->dma_rx); + /* Wait for callback */ + return IRQ_HANDLED; + } stm32h7_spi_disable(spi); spi_finalize_current_transfer(ctrl); } @@ -1423,6 +1447,8 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) /* Enable the interrupts */ if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE; + if (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || spi->cur_comm == SPI_FULL_DUPLEX)) + ier |= STM32H7_SPI_IER_EOTIE; stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier); @@ -1432,6 +1458,119 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART); } +/** + * stm32_spi_prepare_rx_dma_mdma_chaining - Prepare RX DMA and MDMA chaining + * @spi: pointer to the spi controller data structure + * @xfer: pointer to the spi transfer + * @rx_dma_conf: pointer to the DMA configuration for RX channel + * @rx_dma_desc: pointer to the RX DMA descriptor + * @rx_mdma_desc: pointer to the RX MDMA descriptor + * + * It must return 0 if the chaining is possible or an error code if not. + */ +static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, + struct spi_transfer *xfer, + struct dma_slave_config *rx_dma_conf, + struct dma_async_tx_descriptor **rx_dma_desc, + struct dma_async_tx_descriptor **rx_mdma_desc) +{ + struct dma_slave_config rx_mdma_conf = {0}; + u32 sram_period, nents = 0, spi_s_len; + struct sg_table dma_sgt, mdma_sgt; + struct scatterlist *spi_s, *s; + dma_addr_t dma_buf; + int i, ret; + + sram_period = spi->sram_rx_buf_size / 2; + + /* Configure MDMA RX channel */ + rx_mdma_conf.direction = rx_dma_conf->direction; + rx_mdma_conf.src_addr = spi->sram_dma_rx_buf; + rx_mdma_conf.peripheral_config = rx_dma_conf->peripheral_config; + rx_mdma_conf.peripheral_size = rx_dma_conf->peripheral_size; + dmaengine_slave_config(spi->mdma_rx, &rx_mdma_conf); + + /* Count the number of entries needed */ + for_each_sg(xfer->rx_sg.sgl, spi_s, xfer->rx_sg.nents, i) + if (sg_dma_len(spi_s) > sram_period) + nents += DIV_ROUND_UP(sg_dma_len(spi_s), sram_period); + else + nents++; + + /* Prepare DMA slave_sg DBM transfer DEV_TO_MEM (RX>MEM=SRAM) */ + ret = sg_alloc_table(&dma_sgt, nents, GFP_ATOMIC); + if (ret) + return ret; + + spi_s = xfer->rx_sg.sgl; + spi_s_len = sg_dma_len(spi_s); + dma_buf = spi->sram_dma_rx_buf; + for_each_sg(dma_sgt.sgl, s, dma_sgt.nents, i) { + size_t bytes = min_t(size_t, spi_s_len, sram_period); + + sg_dma_len(s) = bytes; + sg_dma_address(s) = dma_buf; + spi_s_len -= bytes; + + if (!spi_s_len && sg_next(spi_s)) { + spi_s = sg_next(spi_s); + spi_s_len = sg_dma_len(spi_s); + dma_buf = spi->sram_dma_rx_buf; + } else { /* DMA configured in DBM: it will swap between the SRAM periods */ + if (i & 1) + dma_buf += sram_period; + else + dma_buf = spi->sram_dma_rx_buf; + } + } + + *rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, + dma_sgt.nents, rx_dma_conf->direction, + DMA_PREP_INTERRUPT); + sg_free_table(&dma_sgt); + + if (!rx_dma_desc) + return -EINVAL; + + /* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */ + ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC); + if (ret) { + rx_dma_desc = NULL; + return ret; + } + + spi_s = xfer->rx_sg.sgl; + spi_s_len = sg_dma_len(spi_s); + dma_buf = sg_dma_address(spi_s); + for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) { + size_t bytes = min_t(size_t, spi_s_len, sram_period); + + sg_dma_len(s) = bytes; + sg_dma_address(s) = dma_buf; + spi_s_len -= bytes; + + if (!spi_s_len && sg_next(spi_s)) { + spi_s = sg_next(spi_s); + spi_s_len = sg_dma_len(spi_s); + dma_buf = sg_dma_address(spi_s); + } else { + dma_buf += bytes; + } + } + + *rx_mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, + mdma_sgt.nents, rx_mdma_conf.direction, + DMA_PREP_INTERRUPT); + sg_free_table(&mdma_sgt); + + if (!rx_mdma_desc) { + rx_dma_desc = NULL; + return -EINVAL; + } + + return 0; +} + /** * stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA * @spi: pointer to the spi controller data structure @@ -1443,38 +1582,43 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, struct spi_transfer *xfer) { + struct dma_async_tx_descriptor *rx_mdma_desc = NULL, *rx_dma_desc = NULL; + struct dma_async_tx_descriptor *tx_dma_desc = NULL; struct dma_slave_config tx_dma_conf, rx_dma_conf; - struct dma_async_tx_descriptor *tx_dma_desc, *rx_dma_desc; unsigned long flags; + int ret = 0; spin_lock_irqsave(&spi->lock, flags); - rx_dma_desc = NULL; if (spi->rx_buf && spi->dma_rx) { stm32_spi_dma_config(spi, spi->dma_rx, &rx_dma_conf, DMA_DEV_TO_MEM); - dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); - - /* Enable Rx DMA request */ - stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg, - spi->cfg->regs->dma_rx_en.mask); - - rx_dma_desc = dmaengine_prep_slave_sg( - spi->dma_rx, xfer->rx_sg.sgl, - xfer->rx_sg.nents, - rx_dma_conf.direction, - DMA_PREP_INTERRUPT); + if (spi->mdma_rx) { + rx_dma_conf.peripheral_size = 1; + dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); + + ret = stm32_spi_prepare_rx_dma_mdma_chaining(spi, xfer, &rx_dma_conf, + &rx_dma_desc, &rx_mdma_desc); + if (ret) { /* RX DMA MDMA chaining not possible, fallback to DMA only */ + rx_dma_conf.peripheral_config = 0; + rx_dma_desc = NULL; + } + } + if (!rx_dma_desc) { + dmaengine_slave_config(spi->dma_rx, &rx_dma_conf); + rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, xfer->rx_sg.sgl, + xfer->rx_sg.nents, + rx_dma_conf.direction, + DMA_PREP_INTERRUPT); + } } - tx_dma_desc = NULL; if (spi->tx_buf && spi->dma_tx) { stm32_spi_dma_config(spi, spi->dma_tx, &tx_dma_conf, DMA_MEM_TO_DEV); dmaengine_slave_config(spi->dma_tx, &tx_dma_conf); - - tx_dma_desc = dmaengine_prep_slave_sg( - spi->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, - tx_dma_conf.direction, - DMA_PREP_INTERRUPT); + tx_dma_desc = dmaengine_prep_slave_sg(spi->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + tx_dma_conf.direction, + DMA_PREP_INTERRUPT); } if ((spi->tx_buf && spi->dma_tx && !tx_dma_desc) || @@ -1485,9 +1629,25 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, goto dma_desc_error; if (rx_dma_desc) { - rx_dma_desc->callback = spi->cfg->dma_rx_cb; - rx_dma_desc->callback_param = spi; + if (rx_mdma_desc) { + rx_mdma_desc->callback = spi->cfg->dma_rx_cb; + rx_mdma_desc->callback_param = spi; + } else { + rx_dma_desc->callback = spi->cfg->dma_rx_cb; + rx_dma_desc->callback_param = spi; + } + /* Enable Rx DMA request */ + stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg, + spi->cfg->regs->dma_rx_en.mask); + if (rx_mdma_desc) { + if (dma_submit_error(dmaengine_submit(rx_mdma_desc))) { + dev_err(spi->dev, "Rx MDMA submit failed\n"); + goto dma_desc_error; + } + /* Enable Rx MDMA channel */ + dma_async_issue_pending(spi->mdma_rx); + } if (dma_submit_error(dmaengine_submit(rx_dma_desc))) { dev_err(spi->dev, "Rx DMA submit failed\n"); goto dma_desc_error; @@ -1522,6 +1682,8 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, return 1; dma_submit_error: + if (spi->mdma_rx) + dmaengine_terminate_sync(spi->mdma_rx); if (spi->dma_rx) dmaengine_terminate_sync(spi->dma_rx); @@ -1533,6 +1695,9 @@ dma_desc_error: dev_info(spi->dev, "DMA issue: fall back to irq transfer\n"); + if (spi->sram_rx_buf) + memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size); + spi->cur_usedma = false; return spi->cfg->transfer_one_irq(spi); } @@ -1891,6 +2056,9 @@ static int stm32_spi_unprepare_msg(struct spi_controller *ctrl, spi->cfg->disable(spi); + if (spi->sram_rx_buf) + memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size); + return 0; } @@ -2245,6 +2413,33 @@ static int stm32_spi_probe(struct platform_device *pdev) if (spi->dma_tx || spi->dma_rx) ctrl->can_dma = stm32_spi_can_dma; + spi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (spi->sram_pool) { + spi->sram_rx_buf_size = gen_pool_size(spi->sram_pool); + dev_info(&pdev->dev, "SRAM pool: %zu KiB for RX DMA/MDMA chaining\n", + spi->sram_rx_buf_size / 1024); + spi->sram_rx_buf = gen_pool_dma_zalloc(spi->sram_pool, spi->sram_rx_buf_size, + &spi->sram_dma_rx_buf); + if (!spi->sram_rx_buf) { + dev_err(&pdev->dev, "failed to allocate SRAM buffer\n"); + } else { + spi->mdma_rx = dma_request_chan(spi->dev, "rxm2m"); + if (IS_ERR(spi->mdma_rx)) { + ret = PTR_ERR(spi->mdma_rx); + spi->mdma_rx = NULL; + if (ret == -EPROBE_DEFER) { + goto err_pool_free; + } else { + gen_pool_free(spi->sram_pool, + (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); + dev_warn(&pdev->dev, + "failed to request rx mdma channel, DMA only\n"); + } + } + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, STM32_SPI_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(&pdev->dev); @@ -2272,6 +2467,11 @@ err_pm_disable: pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + + if (spi->mdma_rx) + dma_release_channel(spi->mdma_rx); +err_pool_free: + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, spi->sram_rx_buf_size); err_dma_release: if (spi->dma_tx) dma_release_channel(spi->dma_tx); @@ -2302,6 +2502,11 @@ static void stm32_spi_remove(struct platform_device *pdev) dma_release_channel(ctrl->dma_tx); if (ctrl->dma_rx) dma_release_channel(ctrl->dma_rx); + if (spi->mdma_rx) + dma_release_channel(spi->mdma_rx); + if (spi->sram_rx_buf) + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); clk_disable_unprepare(spi->clk); -- GitLab From 4956bf44524394211ca80aa04d0c9e1e9bb0219d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:06 +0200 Subject: [PATCH 279/868] spi: stm32: deprecate `st,spi-midi-ns` property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `st,spi-midi-ns` property, which was used to set a nanosecond delay between transferred words, is now deprecated. This functionality is now supported by the SPI framework through the `spi_transfer` struct's `word_delay` variable. Therefore, the private `st,spi-midi-ns` property is no longer needed and has been deprecated in favor of the generic solution. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-5-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 8581f24c111f7..3d20f09f1ae7e 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -283,7 +283,7 @@ struct stm32_spi_cfg { int (*config)(struct stm32_spi *spi); void (*set_bpw)(struct stm32_spi *spi); int (*set_mode)(struct stm32_spi *spi, unsigned int comm_type); - void (*set_data_idleness)(struct stm32_spi *spi, u32 length); + void (*set_data_idleness)(struct stm32_spi *spi, struct spi_transfer *xfer); int (*set_number_of_data)(struct stm32_spi *spi, u32 length); void (*write_tx)(struct stm32_spi *spi); void (*read_rx)(struct stm32_spi *spi); @@ -1880,11 +1880,26 @@ static int stm32h7_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type) * stm32h7_spi_data_idleness - configure minimum time delay inserted between two * consecutive data frames in host mode * @spi: pointer to the spi controller data structure - * @len: transfer len + * @xfer: pointer to spi transfer */ -static void stm32h7_spi_data_idleness(struct stm32_spi *spi, u32 len) +static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer *xfer) { u32 cfg2_clrb = 0, cfg2_setb = 0; + u32 len = xfer->len; + u32 spi_delay_ns; + + spi_delay_ns = spi_delay_to_ns(&xfer->word_delay, xfer); + + if (spi->cur_midi != 0) { + dev_warn(spi->dev, "st,spi-midi-ns DT property is deprecated\n"); + if (spi_delay_ns) { + dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n", + spi_delay_ns); + spi->cur_midi = spi_delay_ns; + } + } else { + spi->cur_midi = spi_delay_ns; + } cfg2_clrb |= STM32H7_SPI_CFG2_MIDI; if ((len > 1) && (spi->cur_midi > 0)) { @@ -1975,7 +1990,7 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, spi->cur_comm = comm_type; if (STM32_SPI_HOST_MODE(spi) && spi->cfg->set_data_idleness) - spi->cfg->set_data_idleness(spi, transfer->len); + spi->cfg->set_data_idleness(spi, transfer); if (spi->cur_bpw <= 8) nb_words = transfer->len; -- GitLab From bd60f94a3eb4f80cb66c9687d640554fd0c579d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:04 +0200 Subject: [PATCH 280/868] spi: dt-bindings: stm32: update bindings with SPI Rx DMA-MDMA chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add MDMA channel, and new sram property which are mandatory to enable SPI Rx DMA-MDMA chaining. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-3-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/st,stm32-spi.yaml | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml index 76e43c0ce36ca..ca880a226afa0 100644 --- a/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml +++ b/Documentation/devicetree/bindings/spi/st,stm32-spi.yaml @@ -18,6 +18,38 @@ maintainers: allOf: - $ref: spi-controller.yaml# + - if: + properties: + compatible: + contains: + const: st,stm32f4-spi + + then: + properties: + st,spi-midi-ns: false + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx + + - if: + properties: + compatible: + contains: + const: st,stm32mp25-spi + + then: + properties: + sram: false + dmas: + maxItems: 2 + dma-names: + items: + - const: rx + - const: tx properties: compatible: @@ -41,16 +73,28 @@ properties: dmas: description: | - DMA specifiers for tx and rx dma. DMA fifo mode must be used. See - the STM32 DMA controllers bindings Documentation/devicetree/bindings/dma/stm32/*.yaml. + DMA specifiers for tx and rx channels. DMA fifo mode must be used. See + the STM32 DMA bindings Documentation/devicetree/bindings/dma/stm32/st,*dma.yaml + minItems: 2 items: - description: rx DMA channel - description: tx DMA channel + - description: rxm2m MDMA channel dma-names: + minItems: 2 items: - const: rx - const: tx + - const: rxm2m + + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandles to a reserved SRAM region which is used as temporary + storage memory between DMA and MDMA engines. + The region should be defined as child node of the AHB SRAM node + as per the generic bindings in Documentation/devicetree/bindings/sram/sram.yaml access-controllers: minItems: 1 -- GitLab From 9a944494c299fabf3cc781798eb7c02a0bece364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 16 Jun 2025 11:21:07 +0200 Subject: [PATCH 281/868] spi: dt-bindings: stm32: deprecate `st,spi-midi-ns` property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendor `st,spi-midi-ns` property is no longer needed and has been deprecated in favor of a generic solution. Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250616-spi-upstream-v1-6-7e8593f3f75d@foss.st.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 8fc17e16efb20..8b6e8fc009dbd 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -115,6 +115,7 @@ properties: maxItems: 4 st,spi-midi-ns: + deprecated: true description: | Only for STM32H7, (Master Inter-Data Idleness) minimum time delay in nanoseconds inserted between two consecutive data frames. -- GitLab From 23b33cf1244185d0432b25afdc04f2fe47a1cc2e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 18 Jun 2025 09:46:53 +0200 Subject: [PATCH 282/868] gpio: clps711x: drop unneeded platform_set_drvdata() There's no corresponding platform_get_drvdata() used in this module or the higher-level gpio-mmio. Let's remove the unneeded call to platform_set_drvdata(). Link: https://lore.kernel.org/r/20250618074653.25555-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-clps711x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index d69a24dd48289..c0c8d29d0dce7 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -62,7 +62,6 @@ static int clps711x_gpio_probe(struct platform_device *pdev) gc->base = -1; gc->owner = THIS_MODULE; - platform_set_drvdata(pdev, gc); return devm_gpiochip_add_data(&pdev->dev, gc, NULL); } -- GitLab From f792733e08d5f5d44ef76d22bcca7ca45a82d0de Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Jun 2025 22:04:28 +0800 Subject: [PATCH 283/868] ASoC: sdw_utils: add component_name string to dai_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the sdw machine driver uses different way to get the component name from the DAI name for different codecs in the rtd_init callback. It means that we need to rely on the rtd_init callback to get the component name. Add an optional component string to the asoc_sdw_dai_info struct allows the machine driver to get the component name directly. The commit adds the component names for the AMP dais for the preparation to set card->components string for combined speaker configs. Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Shuming Fan Link: https://patch.msgid.link/20250625140430.311865-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 1 + sound/soc/sdw_utils/soc_sdw_utils.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index b63021f5afafa..6049a5d0cfcd8 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -46,6 +46,7 @@ struct asoc_sdw_codec_info; struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *dai_name; + const char *component_name; const int dai_type; const int dailink[2]; /* dailink id for each direction */ const struct snd_kcontrol_new *controls; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d75e7292240ba..2e8820137c69a 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -135,6 +135,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt712-sdca-aif2", + .component_name = "rt712", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -275,6 +276,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt1320-aif1", + .component_name = "rt1320", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -409,6 +411,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt722-sdca-aif2", + .component_name = "rt722", .dai_type = SOC_SDW_DAI_TYPE_AMP, /* No feedback capability is provided by rt722-sdca codec driver*/ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -438,6 +441,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "max98373-aif1", + .component_name = "mx8373", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_maxim_init, @@ -456,6 +460,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "max98363-aif1", + .component_name = "mx8363", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_maxim_init, @@ -491,6 +496,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs_amp_init, @@ -516,6 +522,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs35l56-sdw1", + .component_name = "cs35l56", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs_amp_init, -- GitLab From 0f60ecffbfe35e12eb56c99640ba2360244b5bb3 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 25 Jun 2025 22:04:29 +0800 Subject: [PATCH 284/868] ASoC: sdw_utils: generate combined spk components string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The suggestion from UCM for 2 speaker components is using 1 "spk" tag with 2 component names. Like "spk:rt722+rt1320". The commit removes the creation of the "spk" components in each rtd_init callback and creat the string in asoc_sdw_rtd_init(). Signed-off-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Shuming Fan Link: https://patch.msgid.link/20250625140430.311865-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_cs_amp.c | 10 --------- sound/soc/sdw_utils/soc_sdw_maxim.c | 9 -------- sound/soc/sdw_utils/soc_sdw_rt_amp.c | 6 ------ sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c | 7 ------- sound/soc/sdw_utils/soc_sdw_utils.c | 26 ++++++++++++++++++++++++ 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c index 35b550bcd4ded..520ea36c63eee 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c @@ -14,7 +14,6 @@ #include #include -#define CODEC_NAME_SIZE 8 #define CS_AMP_CHANNELS_PER_AMP 4 #define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */ @@ -38,21 +37,12 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs35l56_volume_limit, "SND_SOC_SDW_UTILS"); int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { - const char *dai_name = rtd->dai_link->codecs->dai_name; struct snd_soc_card *card = rtd->card; - char codec_name[CODEC_NAME_SIZE]; char widget_name[16]; struct snd_soc_dapm_route route = { "Speaker", NULL, widget_name }; struct snd_soc_dai *codec_dai; int i, ret; - snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - for_each_rtd_codec_dais(rtd, i, codec_dai) { if (!strstr(codec_dai->name, "cs35l56")) continue; diff --git a/sound/soc/sdw_utils/soc_sdw_maxim.c b/sound/soc/sdw_utils/soc_sdw_maxim.c index 5df8d9cae60c3..8f9d1ed0725bd 100644 --- a/sound/soc/sdw_utils/soc_sdw_maxim.c +++ b/sound/soc/sdw_utils/soc_sdw_maxim.c @@ -28,15 +28,6 @@ int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_ struct snd_soc_card *card = rtd->card; int ret; - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:mx%04x", - card->components, maxim_part_id); - if (!card->components) - return -ENOMEM; - - dev_dbg(card->dev, "soundwire maxim card components assigned : %s\n", - card->components); - ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, 2); if (ret) dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); diff --git a/sound/soc/sdw_utils/soc_sdw_rt_amp.c b/sound/soc/sdw_utils/soc_sdw_rt_amp.c index 83c2368170cb5..76ee24b8eee4a 100644 --- a/sound/soc/sdw_utils/soc_sdw_rt_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_rt_amp.c @@ -195,12 +195,6 @@ int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc rt_amp_map = get_codec_name_and_route(dai, codec_name); - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - for_each_rtd_codec_dais(rtd, i, codec_dai) { if (strstr(codec_dai->component->name_prefix, "-1")) ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); diff --git a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c index 0161b14297d5a..224b58de90849 100644 --- a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c +++ b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c @@ -73,13 +73,6 @@ int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd return -EINVAL; } - /* Update card components */ - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:%s", - card->components, codec_name); - if (!card->components) - return -ENOMEM; - /* Add routes */ ret = snd_soc_dapm_add_routes(&card->dapm, route_map->route_map, route_map->route_size); if (ret) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 2e8820137c69a..a744ca019378a 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -740,6 +740,7 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; struct asoc_sdw_codec_info *codec_info; struct snd_soc_dai *dai; + const char *spk_components=""; int dai_index; int ret; int i; @@ -792,7 +793,32 @@ skip_add_controls_widgets: if (ret) return ret; } + + /* Generate the spk component string for card->components string */ + if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP && + codec_info->dais[dai_index].component_name) { + if (strlen (spk_components) == 0) + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, "%s", + codec_info->dais[dai_index].component_name); + else + /* Append component name to spk_components */ + spk_components = + devm_kasprintf(card->dev, GFP_KERNEL, + "%s+%s", spk_components, + codec_info->dais[dai_index].component_name); + } + codec_info->dais[dai_index].rtd_init_done = true; + + } + + if (strlen (spk_components) > 0) { + /* Update card components for speaker components */ + card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s", + card->components, spk_components); + if (!card->components) + return -ENOMEM; } return 0; -- GitLab From 28a9ab01f6ac87afb78cb18098bc37a74db0d8f0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 25 Jun 2025 10:05:46 +0200 Subject: [PATCH 285/868] gpio: rcar: Remove checks for empty bankmasks The GPIO core never passes empty bankmasks to the callbacks for handling multiple signals at once. Remove the superfluous checks, and refactor the code. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/29fb200d3f92e79cdd5ce4048d2847c265f337b4.1750838486.git.geert+renesas@glider.be Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 18c965ee02c8d..8416e0430887b 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -331,14 +331,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { + u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); struct gpio_rcar_priv *p = gpiochip_get_data(chip); - u32 bankmask, outputs, m, val = 0; + u32 outputs, m, val = 0; unsigned long flags; - bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (!bankmask) - return 0; - if (p->info.has_always_in) { bits[0] = gpio_rcar_read(p, INDT) & bankmask; return 0; @@ -372,13 +369,10 @@ static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { + u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); struct gpio_rcar_priv *p = gpiochip_get_data(chip); unsigned long flags; - u32 val, bankmask; - - bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); - if (!bankmask) - return; + u32 val; raw_spin_lock_irqsave(&p->lock, flags); val = gpio_rcar_read(p, OUTDT); -- GitLab From 3315e39e5639ac770782b658e499d45f68ea7d82 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 25 Jun 2025 10:05:47 +0200 Subject: [PATCH 286/868] gpio: rcar: Use new line value setter callbacks struct gpio_chip now has callbacks for setting line values that return integers, so they can indicate failures. Convert the driver to using them. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/f09a0481fc0ddafb9aa05d903fbb42ef52332c03.1750838486.git.geert+renesas@glider.be Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 8416e0430887b..1d121a4275905 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -356,7 +356,7 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, return 0; } -static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) +static int gpio_rcar_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_rcar_priv *p = gpiochip_get_data(chip); unsigned long flags; @@ -364,10 +364,12 @@ static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) raw_spin_lock_irqsave(&p->lock, flags); gpio_rcar_modify_bit(p, OUTDT, offset, value); raw_spin_unlock_irqrestore(&p->lock, flags); + + return 0; } -static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { u32 bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); struct gpio_rcar_priv *p = gpiochip_get_data(chip); @@ -380,6 +382,8 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask, val |= (bankmask & bits[0]); gpio_rcar_write(p, OUTDT, val); raw_spin_unlock_irqrestore(&p->lock, flags); + + return 0; } static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, @@ -531,8 +535,8 @@ static int gpio_rcar_probe(struct platform_device *pdev) gpio_chip->get = gpio_rcar_get; gpio_chip->get_multiple = gpio_rcar_get_multiple; gpio_chip->direction_output = gpio_rcar_direction_output; - gpio_chip->set = gpio_rcar_set; - gpio_chip->set_multiple = gpio_rcar_set_multiple; + gpio_chip->set_rv = gpio_rcar_set; + gpio_chip->set_multiple_rv = gpio_rcar_set_multiple; gpio_chip->label = name; gpio_chip->parent = dev; gpio_chip->owner = THIS_MODULE; -- GitLab From 80744a3bed8ce65071ca6e970a5f8b7c12213d3d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 25 Jun 2025 13:57:52 -0500 Subject: [PATCH 287/868] ACPI: APEI: EINJ: prevent memory corruption in error_type_set() The "einj_buf" buffer is 32 chars. If "count" is larger than that it results in memory corruption. Cap it at 31 so that we leave the last character as a NUL terminator. By the way, the highest reasonable value for "count" is 24. Fixes: 0c6176e1e186 ("ACPI: APEI: EINJ: Enable the discovery of EINJv2 capabilities") Signed-off-by: Dan Carpenter Reviewed-by: Ira Weiny Link: https://patch.msgid.link/ae6286cf-4d73-4b97-8c0f-0782a65b8f51@sabinyo.mountain Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index d6d7e36e36479..f5cfa6310f0e1 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -826,6 +826,10 @@ static ssize_t error_type_set(struct file *file, const char __user *buf, int rc; u64 val; + /* Leave the last character for the NUL terminator */ + if (count > sizeof(einj_buf) - 1) + return -EINVAL; + memset(einj_buf, 0, sizeof(einj_buf)); if (copy_from_user(einj_buf, buf, count)) return -EFAULT; -- GitLab From c13d38bc9b00eaab19ba4d7608677371d2d2f480 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 24 Jun 2025 21:10:32 +0100 Subject: [PATCH 288/868] ACPI: APEI: EINJ: Fix less than zero comparison on a size_t variable The check for c < 0 is always false because variable c is a size_t which is not a signed type. Fix this by making c a ssize_t. Fixes: 90711f7bdf76 ("ACPI: APEI: EINJ: Create debugfs files to enter device id and syndrome") Signed-off-by: Colin Ian King Reviewed-by: Ira Weiny Link: https://patch.msgid.link/20250624201032.522168-1-colin.i.king@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index f5cfa6310f0e1..59777a292ffcb 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -913,7 +913,7 @@ static ssize_t u128_write(struct file *f, const char __user *buf, size_t count, u8 tmp[COMPONENT_LEN]; char byte[3] = {}; char *s, *e; - size_t c; + ssize_t c; long val; int i; -- GitLab From 0fd0541b6770b190afa9534c9208c0aa774d533c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 24 Jun 2025 21:29:37 +0100 Subject: [PATCH 289/868] ACPI: APEI: EINJ: Fix check and iounmap of uninitialized pointer p In the case where a request_mem_region call fails and pointer r is null the error exit path via label 'out' will check for a non-null pointer p and try to iounmap it. However, pointer p has not been assigned a value at this point, so it may potentially contain any garbage value. Fix this by ensuring pointer p is initialized to NULL. Fixes: 1a35c88302a3 ("ACPI: APEI: EINJ: Fix kernel test sparse warnings") Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250624202937.523013-1-colin.i.king@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 59777a292ffcb..3d37978418e87 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -401,7 +401,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, u32 table_size; int rc = -EIO; struct acpi_generic_address *trigger_param_region = NULL; - struct acpi_einj_trigger __iomem *p; + struct acpi_einj_trigger __iomem *p = NULL; r = request_mem_region(trigger_paddr, sizeof(trigger_tab), "APEI EINJ Trigger Table"); -- GitLab From ef4af870be41a2a741109b99bbf780c769dcdc30 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Sun, 8 Jun 2025 22:28:34 -0500 Subject: [PATCH 290/868] ACPICA: Decrease `AcpiExTracePoint` verbosity Early in kernel boot pointers can't be used and so %p shows up incorrectly: ``` extrace-0138 ex_trace_point : Method Begin [0x(____ptrval____):\M460] execution. ``` Later in the boot %p works, but it's not really actually useful when the pathname can resolve properly. Adjust the debug print so that if the Pathname resolves that the pointer isn't also printed: ``` extrace-0138 ex_trace_point : Method Begin [\M460] execution. ``` Link: https://github.com/acpica/acpica/pull/1013 Link: https://github.com/acpica/acpica/commit/bdc2a4e646f097b693aa60f1f2c4228d1e31b0d1 Signed-off-by: Mario Limonciello Link: https://patch.msgid.link/20250609032839.525087-1-superm1@kernel.org Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/extrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c index d34497f3576a4..36934d4f26fb4 100644 --- a/drivers/acpi/acpica/extrace.c +++ b/drivers/acpi/acpica/extrace.c @@ -136,9 +136,9 @@ acpi_ex_trace_point(acpi_trace_event_type type, if (pathname) { ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, - "%s %s [0x%p:%s] execution.\n", + "%s %s [%s] execution.\n", acpi_ex_get_trace_event_name(type), - begin ? "Begin" : "End", aml, pathname)); + begin ? "Begin" : "End", pathname)); } else { ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT, "%s %s [0x%p] execution.\n", -- GitLab From bb4049c9fe94f6b257cf4c211b6df398487da6bf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jun 2025 23:11:25 +0300 Subject: [PATCH 291/868] ACPI: wakeup: Drop unneeded casting for sleep_state Back to the original patch [1] sleep_state was defined as a custom acpi_integer type variable. Nowadays it's plain u64. No need to have casting for it anymore. Link: https://bugzilla.kernel.org/show_bug.cgi?id=1415 [1] Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250612201321.3536493-2-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/wakeup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/wakeup.c b/drivers/acpi/wakeup.c index b02bf770aead3..ff6dc957bc113 100644 --- a/drivers/acpi/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -42,7 +42,7 @@ void acpi_enable_wakeup_devices(u8 sleep_state) list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, wakeup_list) { if (!dev->wakeup.flags.valid - || sleep_state > (u32) dev->wakeup.sleep_state + || sleep_state > dev->wakeup.sleep_state || !(device_may_wakeup(&dev->dev) || dev->wakeup.prepare_count)) continue; @@ -67,7 +67,7 @@ void acpi_disable_wakeup_devices(u8 sleep_state) list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, wakeup_list) { if (!dev->wakeup.flags.valid - || sleep_state > (u32) dev->wakeup.sleep_state + || sleep_state > dev->wakeup.sleep_state || !(device_may_wakeup(&dev->dev) || dev->wakeup.prepare_count)) continue; -- GitLab From b32a54336595d52408b9f0678b5e308dca2d1f40 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jun 2025 23:11:26 +0300 Subject: [PATCH 292/868] ACPI: proc: Use correct format specifier and drop casting The format string in acpi_system_wakeup_device_seq_show() uses incorrect specifier along with explicit (unneeded) casting. Drop the latter and update the former. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250612201321.3536493-3-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 4322f2da6d10d..48215ba091930 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -30,9 +30,9 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) if (!dev->wakeup.flags.valid) continue; - seq_printf(seq, "%s\t S%d\t", + seq_printf(seq, "%s\t S%llu\t", dev->pnp.bus_id, - (u32) dev->wakeup.sleep_state); + dev->wakeup.sleep_state); mutex_lock(&dev->physical_node_lock); -- GitLab From 934eee0ce35cde5ea8a570de94cf5903cb44518d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jun 2025 23:11:27 +0300 Subject: [PATCH 293/868] ACPI: proc: Remove unused header With `make W=1` build we get a warning: drivers/acpi/proc.c: warning: EXPORT_SYMBOL() is not used, but #include is present Fix it by removing unused inclusion. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250612201321.3536493-4-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/proc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 48215ba091930..8ae85b06c4220 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include #include -#include #include #include #include -- GitLab From 86dc11cd6f5cb19ea9d33610216acfa786e09b1d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jun 2025 23:11:28 +0300 Subject: [PATCH 294/868] ACPI: proc: Use str_enabled_disabled() helper Replace ternary (condition ? "enabled" : "disabled") with str_enabled_disabled() from string_choices.h to improve readability, maintain uniform string usage, and reduce binary size through linker deduplication. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250612201321.3536493-5-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/proc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 8ae85b06c4220..440150c67ba69 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -38,8 +39,7 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) if (!dev->physical_node_count) { seq_printf(seq, "%c%-8s\n", dev->wakeup.flags.valid ? '*' : ' ', - device_may_wakeup(&dev->dev) ? - "enabled" : "disabled"); + str_enabled_disabled(device_may_wakeup(&dev->dev))); } else { struct device *ldev; list_for_each_entry(entry, &dev->physical_node_list, @@ -54,9 +54,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "%c%-8s %s:%s\n", dev->wakeup.flags.valid ? '*' : ' ', - (device_may_wakeup(&dev->dev) || - device_may_wakeup(ldev)) ? - "enabled" : "disabled", + str_enabled_disabled(device_may_wakeup(ldev) || + device_may_wakeup(&dev->dev)), ldev->bus ? ldev->bus->name : "no-bus", dev_name(ldev)); put_device(ldev); -- GitLab From acec3f6aa4f2bd9287a118f97ffa4e57c22f6344 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 12 Jun 2025 23:11:29 +0300 Subject: [PATCH 295/868] ACPI: proc: Prefer to use octal permission Octal permissions are preferred over the symbolics ones for readbility. This ceases warning message pointed by checkpatch. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250612201321.3536493-6-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/proc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 440150c67ba69..c08ead07252bc 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -139,6 +139,5 @@ static const struct proc_ops acpi_system_wakeup_device_proc_ops = { void __init acpi_sleep_proc_init(void) { /* 'wakeup device' [R/W] */ - proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, - acpi_root_dir, &acpi_system_wakeup_device_proc_ops); + proc_create("wakeup", 0644, acpi_root_dir, &acpi_system_wakeup_device_proc_ops); } -- GitLab From 08bf1663c21a3e815eda28fa242d84c945ca3b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Tue, 10 Jun 2025 10:22:53 +0200 Subject: [PATCH 296/868] dmaengine: Add devm_dma_request_chan() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the arsenal of devm functions for DMA devices, this time for requesting channels. Signed-off-by: Bence Csókás Link: https://lore.kernel.org/r/20250610082256.400492-2-csokas.bence@prolan.hu Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 30 ++++++++++++++++++++++++++++++ include/linux/dmaengine.h | 7 +++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 758fcd0546d8b..ca13cd39330ba 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -926,6 +926,36 @@ void dma_release_channel(struct dma_chan *chan) } EXPORT_SYMBOL_GPL(dma_release_channel); +static void dmaenginem_release_channel(void *chan) +{ + dma_release_channel(chan); +} + +/** + * devm_dma_request_chan - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + * + * Returns pointer to appropriate DMA channel on success or an error pointer. + * + * The operation is managed and will be undone on driver detach. + */ + +struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name) +{ + struct dma_chan *chan = dma_request_chan(dev, name); + int ret = 0; + + if (!IS_ERR(chan)) + ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan); + + if (ret) + return ERR_PTR(ret); + + return chan; +} +EXPORT_SYMBOL_GPL(devm_dma_request_chan); + /** * dmaengine_get - register interest in dma_channels */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index bb146c5ac3e4c..6de7c05d6bd8c 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1524,6 +1524,7 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, struct dma_chan *dma_request_chan(struct device *dev, const char *name); struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask); +struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name); void dma_release_channel(struct dma_chan *chan); int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps); @@ -1560,6 +1561,12 @@ static inline struct dma_chan *dma_request_chan_by_mask( { return ERR_PTR(-ENODEV); } + +static inline struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name) +{ + return ERR_PTR(-ENODEV); +} + static inline void dma_release_channel(struct dma_chan *chan) { } -- GitLab From ce57bc9771411d6d27f2ca7b40396cbd7d684ba9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jun 2025 18:23:07 +0300 Subject: [PATCH 297/868] regulator: core: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Note that kernel.h is discouraged to be included as it's written at the top of that file. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250626152307.322627-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- include/linux/regulator/coupler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h index 73291f280a23e..5e314a4294fb5 100644 --- a/include/linux/regulator/coupler.h +++ b/include/linux/regulator/coupler.h @@ -8,7 +8,8 @@ #ifndef __LINUX_REGULATOR_COUPLER_H_ #define __LINUX_REGULATOR_COUPLER_H_ -#include +#include +#include #include struct regulator_coupler; -- GitLab From 2555691165a0285a4617230fed859f20dcc51608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Tue, 10 Jun 2025 10:22:54 +0200 Subject: [PATCH 298/868] spi: atmel-quadspi: Use `devm_dma_request_chan()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leave releasing of DMA channels up to the devm facilities. This way we can eliminate the rest of the "goto ladder". Signed-off-by: Bence Csókás Link: https://patch.msgid.link/20250610082256.400492-3-csokas.bence@prolan.hu Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 48 ++++++++++--------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 2f67973242279..fc555c0ce52e1 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -1285,18 +1285,21 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); int ret; - aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx"); + aq->rx_chan = devm_dma_request_chan(&aq->pdev->dev, "rx"); if (IS_ERR(aq->rx_chan)) { ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan), "RX DMA channel is not available\n"); - goto null_rx_chan; + aq->rx_chan = NULL; + return ret; } - aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx"); + aq->tx_chan = devm_dma_request_chan(&aq->pdev->dev, "tx"); if (IS_ERR(aq->tx_chan)) { ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->tx_chan), "TX DMA channel is not available\n"); - goto release_rx_chan; + aq->rx_chan = NULL; + aq->tx_chan = NULL; + return ret; } ctrl->dma_rx = aq->rx_chan; @@ -1307,21 +1310,6 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) dma_chan_name(aq->tx_chan), dma_chan_name(aq->rx_chan)); return 0; - -release_rx_chan: - dma_release_channel(aq->rx_chan); - aq->tx_chan = NULL; -null_rx_chan: - aq->rx_chan = NULL; - return ret; -} - -static void atmel_qspi_dma_release(struct atmel_qspi *aq) -{ - if (aq->rx_chan) - dma_release_channel(aq->rx_chan); - if (aq->tx_chan) - dma_release_channel(aq->tx_chan); } static const struct atmel_qspi_ops atmel_qspi_ops = { @@ -1426,14 +1414,13 @@ static int atmel_qspi_probe(struct platform_device *pdev) /* Request the IRQ */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - err = irq; - goto dma_release; - } + if (irq < 0) + return irq; + err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt, 0, dev_name(&pdev->dev), aq); if (err) - goto dma_release; + return err; pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); @@ -1442,22 +1429,16 @@ static int atmel_qspi_probe(struct platform_device *pdev) err = atmel_qspi_init(aq); if (err) - goto dma_release; + return err; err = spi_register_controller(ctrl); if (err) - goto dma_release; + return err; pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; - -dma_release: - if (aq->caps->has_dma) - atmel_qspi_dma_release(aq); - - return err; } static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq) @@ -1507,9 +1488,6 @@ static void atmel_qspi_remove(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret >= 0) { - if (aq->caps->has_dma) - atmel_qspi_dma_release(aq); - if (aq->caps->has_gclk) { ret = atmel_qspi_sama7g5_suspend(aq); if (ret) -- GitLab From ac4c064f67d3cdf9118b9b09c1e3b28b6c10a7ea Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 25 Jun 2025 17:52:54 -0400 Subject: [PATCH 299/868] spi: dt-bindings: add nxp,lpc3220-spi.yaml Add lpc3220 spi controller binding doc to fix below CHECK_DTBS warning: arch/arm/boot/dts/nxp/lpc/lpc3250-ea3250.dtb: /ahb/apb/spi@20088000: failed to match any schema with compatible: ['nxp,lpc3220-spi'] Signed-off-by: Frank Li Link: https://patch.msgid.link/20250625215255.2640538-1-Frank.Li@nxp.com Signed-off-by: Mark Brown --- .../bindings/spi/nxp,lpc3220-spi.yaml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml new file mode 100644 index 0000000000000..d5f780912f21e --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/nxp,lpc3220-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC3220 SPI controller + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +allOf: + - $ref: spi-controller.yaml# + +unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + +examples: + - | + #include + + spi@20088000 { + compatible = "nxp,lpc3220-spi"; + reg = <0x20088000 0x1000>; + clocks = <&clk LPC32XX_CLK_SPI1>; + #address-cells = <1>; + #size-cells = <0>; + }; + -- GitLab From 29ddce17e909779633f856ad1c2f111fbf71c0df Mon Sep 17 00:00:00 2001 From: Weidong Wang Date: Fri, 27 Jun 2025 19:03:06 +0800 Subject: [PATCH 300/868] ASoC: codecs: Add calibration function to aw88399 chip Add calibration functionality to the aw88399 chip. When the chip is in calibration condition, calibration can be achieved by configuring the chip's internal DSP and save the calibration values in cali_re. Signed-off-by: Weidong Wang Link: https://patch.msgid.link/20250627110306.23488-1-wangweidong.a@awinic.com Signed-off-by: Mark Brown --- sound/soc/codecs/aw88395/aw88395_device.h | 21 ++ sound/soc/codecs/aw88399.c | 440 +++++++++++++++++++++- sound/soc/codecs/aw88399.h | 34 ++ 3 files changed, 493 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h index 0f750f654f3e6..6f8b30b475dac 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.h +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -102,6 +102,11 @@ struct aw_profctrl_desc { unsigned int cur_mode; }; +enum { + CALI_RESULT_NORMAL, + CALI_RESULT_ERROR, +}; + struct aw_volume_desc { unsigned int init_volume; unsigned int mute_volume; @@ -124,9 +129,25 @@ struct aw_cali_delay_desc { unsigned int delay; }; +#define AW_CALI_CFG_NUM (4) +struct cali_cfg { + uint32_t data[AW_CALI_CFG_NUM]; +}; + +struct aw_cali_backup_desc { + unsigned int dsp_ng_cfg; + unsigned int dsp_lp_cfg; +}; + struct aw_cali_desc { u32 cali_re; u32 ra; + bool cali_switch; + bool cali_running; + uint16_t cali_result; + uint16_t store_vol; + struct cali_cfg cali_cfg; + struct aw_cali_backup_desc backup_info; }; struct aw_container { diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index 4b90133e5ab49..bad3ad6b8c0e1 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "aw88399.h" #include "aw88395/aw88395_device.h" @@ -45,6 +46,67 @@ static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, return 0; } +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW88395_DSP_16_DATA_MASK; + ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data); + if (ret) + dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret); + + return ret; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + unsigned int reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + dsp_addr, dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed", + dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state */ + if (regmap_read(aw_dev->regmap, 0x00, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, unsigned short dsp_addr, unsigned int *dsp_data) { @@ -452,14 +514,14 @@ static int aw_dev_set_vcalb(struct aw88399 *aw88399) case AW88399_DEV_VDSEL_VSENSE: ret = aw88399_dev_get_vcalk(aw88399, &vcalk); vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR / + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * ical_k / vcal_k * aw88399->vcalb_init_val; break; case AW88399_DEV_VDSEL_DAC: ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / - AW88399_ISCAL_DAC_FACTOR / ical_k / + AW88399_ISCAL_DAC_FACTOR * ical_k / vcal_k * aw88399->vcalb_init_val; break; default: @@ -1356,6 +1418,329 @@ static struct snd_soc_dai_driver aw88399_dai[] = { }, }; +static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result) +{ + if (cali_result == CALI_RESULT_ERROR) + aw88399_dev_mute(aw_dev, true); + else if (cali_result == CALI_RESULT_NORMAL) + aw88399_dev_mute(aw_dev, false); +} + +static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev) +{ + struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH, + &cali_cfg->data[0], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH, + &cali_cfg->data[1], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN, + &cali_cfg->data[2], AW88399_DSP_16_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA, + &cali_cfg->data[3], AW88399_DSP_16_DATA); + + return ret; +} + +static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev, + struct cali_cfg cali_cfg) +{ + int ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH, + cali_cfg.data[0], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH, + cali_cfg.data[1], AW88399_DSP_32_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN, + cali_cfg.data[2], AW88399_DSP_16_DATA); + if (ret) + return ret; + + ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA, + cali_cfg.data[3], AW88399_DSP_16_DATA); + + return ret; +} + +static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en) +{ + struct cali_cfg set_cfg; + int ret; + + aw_dev_dsp_enable(aw_dev, false); + if (cali_en) { + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE); + aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW88399_DSP_16_DATA); + + ret = aw_cali_svc_get_cali_cfg(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "get cali cfg failed\n"); + aw_dev_dsp_enable(aw_dev, true); + return ret; + } + set_cfg.data[0] = 0; + set_cfg.data[1] = 0; + set_cfg.data[2] = -1; + set_cfg.data[3] = 1; + + ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg); + if (ret) { + dev_err(aw_dev->dev, "set cali cfg failed\n"); + aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg); + aw_dev_dsp_enable(aw_dev, true); + return ret; + } + } else { + aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg); + } + + aw_dev_dsp_enable(aw_dev, true); + + return 0; +} + +static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable) +{ + unsigned int reg_val; + int ret; + + if (enable) { + ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG); + return ret; + } + + aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK); + ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG, + ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG, + ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol); + } + + return ret; +} + +static void aw_cali_svc_backup_info(struct aw_device *aw_dev) +{ + struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info; + unsigned int reg_val, dsp_val; + + regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, ®_val); + backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK); + + aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + &dsp_val, AW88399_DSP_16_DATA); + + backup_desc->dsp_lp_cfg = dsp_val; +} + +static void aw_cali_svc_recover_info(struct aw_device *aw_dev) +{ + struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info; + + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg); + + aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR, + backup_desc->dsp_lp_cfg, AW88399_DSP_16_DATA); +} + +static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start\n"); + return ret; + } + + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status error\n"); + return ret; + } + + aw_cali_svc_backup_info(aw_dev); + ret = aw_cali_svc_cali_en(aw_dev, true); + if (ret) { + dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n"); + return ret; + } + + ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true); + if (ret) { + aw_cali_svc_cali_en(aw_dev, false); + return ret; + } + + } else { + aw_cali_svc_cali_run_dsp_vol(aw_dev, false); + aw_cali_svc_recover_info(aw_dev); + aw_cali_svc_cali_en(aw_dev, false); + } + + return 0; +} + +static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re) +{ + uint32_t dsp_re, show_re; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW88399_DSP_16_DATA); + if (ret) + return ret; + + show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT); + + *re = (uint32_t)(show_re - aw_dev->cali_desc.ra); + + return 0; +} + +static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re) +{ + int sum = 0, i; + + for (i = 1; i < data_size - 1; i++) + sum += data[i]; + + *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM); +} + +static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev) +{ + unsigned int reg_data; + int ret, i; + + for (i = 0; i < AW_GET_IV_CNT_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, ®_data); + if (ret) { + dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG); + return ret; + } + + reg_data &= (~AW88399_REABS_MASK); + if (!reg_data) + return 0; + msleep(30); + } + + dev_err(aw_dev->dev, "IV data abnormal, please check\n"); + + return -EINVAL; +} + +static int compare_ints(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev) +{ + uint32_t re_temp[AW_CALI_READ_CNT_MAX]; + uint32_t dsp_re; + int ret, i; + + for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) { + ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]); + if (ret) + goto cali_re_fail; + msleep(30); + } + + sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL); + + aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re); + + ret = aw_cali_svc_get_iv_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "get iv data failed"); + goto cali_re_fail; + } + + if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) { + dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re); + aw_dev->cali_desc.cali_re = dsp_re; + aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR; + aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result); + + return 0; + } + + aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL; + + aw_dev->cali_desc.cali_re = dsp_re; + dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re); + + aw_dev_dsp_enable(aw_dev, false); + aw_dev_update_cali_re(&aw_dev->cali_desc); + aw_dev_dsp_enable(aw_dev, true); + + return 0; + +cali_re_fail: + aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR; + aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result); + return -EINVAL; +} + +static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_cali_desc *cali_desc = &aw_dev->cali_desc; + int ret; + + if (cali_desc->cali_running) { + dev_err(aw_dev->dev, "calibration in progress\n"); + return -EINVAL; + } + + cali_desc->cali_running = true; + aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL); + + ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true); + if (ret) { + dev_err(aw_dev->dev, "start cali re failed\n"); + goto re_mode_err; + } + + msleep(1000); + + ret = aw_cali_svc_get_smooth_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "get cali re failed\n"); + + aw_cali_svc_cali_re_mode_enable(aw_dev, false); + +re_mode_err: + cali_desc->cali_running = false; + + return ret; +} + static int aw88399_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1616,6 +2001,53 @@ static int aw88399_re_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch; + + return 0; +} + +static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0]) + return 0; + + aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0]; + + return 1; +} + +static int aw88399_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* do nothing */ + return 0; +} + +static int aw88399_calib_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88399->aw_pa; + + if (aw_dev->status && aw_dev->cali_desc.cali_switch) + aw_cali_svc_dev_cali_re(aw88399); + + return 0; +} + static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) { struct aw_device *aw_dev = aw88399->aw_pa; @@ -1708,6 +2140,10 @@ static const struct snd_kcontrol_new aw88399_controls[] = { aw88399_get_fade_out_time, aw88399_set_fade_out_time), SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0, aw88399_re_get, aw88399_re_set), + SOC_SINGLE_BOOL_EXT("Calib Switch", 0, + aw88399_calib_switch_get, aw88399_calib_switch_set), + SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0, + aw88399_calib_get, aw88399_calib_set), AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info, aw88399_profile_get, aw88399_profile_set), }; diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index 5e9cdf725d3dd..cacc03b1eefa6 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -451,6 +451,24 @@ #define AW88399_WDT_CNT_MASK \ (~(((1< Date: Fri, 20 Jun 2025 14:58:01 +0200 Subject: [PATCH 301/868] gpio: constify arguments of gpiod_is_equal() This function is not meant to modify the GPIO descriptors in any way so we can safely constify both arguments. Link: https://lore.kernel.org/r/20250620-gpiod-is-equal-improv-v1-1-a75060505d2c@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- include/linux/gpio/consumer.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5b0b4fc975435..6b4f94c3887fc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -286,7 +286,7 @@ EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); * Returns: * True if the descriptors refer to the same physical pin. False otherwise. */ -bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +bool gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) { return desc == other; } diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index f0b1982da0cc7..00df68c514051 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -181,7 +181,8 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, enum gpiod_flags flags, const char *label); -bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other); +bool gpiod_is_equal(const struct gpio_desc *desc, + const struct gpio_desc *other); #else /* CONFIG_GPIOLIB */ @@ -551,7 +552,7 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, } static inline bool -gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) { WARN_ON(desc || other); return false; -- GitLab From 26981e8906bb5c902e2d34874f64ecfa975d28c8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 20 Jun 2025 14:58:02 +0200 Subject: [PATCH 302/868] gpio: make gpiod_is_equal() arguments stricter It makes no sense for a GPIO descriptor comparator to return true when the arguments passed to it are NULL or IS_ERR(). Let's validate both and return false unless both are valid GPIO descriptors. Suggested-by: Andy Shevchenko Link: https://lore.kernel.org/all/Z_aFBfjb17JxOwyk@black.fi.intel.com/ Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250620-gpiod-is-equal-improv-v1-2-a75060505d2c@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6b4f94c3887fc..7b2174b102199 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -278,20 +278,6 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); -/** - * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. - * @desc: Descriptor to compare. - * @other: The second descriptor to compare against. - * - * Returns: - * True if the descriptors refer to the same physical pin. False otherwise. - */ -bool gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) -{ - return desc == other; -} -EXPORT_SYMBOL_GPL(gpiod_is_equal); - /** * gpio_device_get_base() - Get the base GPIO number allocated by this device * @gdev: GPIO device @@ -400,6 +386,21 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) return; \ } while (0) +/** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(const struct gpio_desc *desc, const struct gpio_desc *other) +{ + return validate_desc(desc, __func__) > 0 && + !IS_ERR_OR_NULL(other) && desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); + static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) { int ret; -- GitLab From 5bcfc4ef40dabcd16a0b736fea7f0d00a9efdbfb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 24 Jun 2025 16:32:18 +0200 Subject: [PATCH 303/868] power: sequencing: thead-gpu: add missing header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using kcalloc(), kfree() etc., we need to include linux/slab.h. While on some architectures it may work fine because the header is pulled in implicitly, on others it triggers the following errors: drivers/power/sequencing/pwrseq-thead-gpu.c: In function ‘pwrseq_thead_gpu_match’: drivers/power/sequencing/pwrseq-thead-gpu.c:147:21: error: implicit declaration of function ‘kcalloc’ [-Wimplicit-function-declaration] 147 | ctx->clks = kcalloc(ctx->num_clks, sizeof(*ctx->clks), GFP_KERNEL); Fixes: d4c2d9b5b7ce ("power: sequencing: Add T-HEAD TH1520 GPU power sequencer driver") Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20250624-pwrseq-match-defines-v1-1-a59d90a951f1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-thead-gpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/sequencing/pwrseq-thead-gpu.c b/drivers/power/sequencing/pwrseq-thead-gpu.c index 3dd27c32020a6..855c6cc4f3b5b 100644 --- a/drivers/power/sequencing/pwrseq-thead-gpu.c +++ b/drivers/power/sequencing/pwrseq-thead-gpu.c @@ -21,6 +21,7 @@ #include #include #include +#include #include -- GitLab From 1a7312b93ab023f68b48a1550049a4f850c2c808 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 24 Jun 2025 16:32:19 +0200 Subject: [PATCH 304/868] power: sequencing: extend build coverage with COMPILE_TEST=y Enable building the pwrseq drivers with COMPILE_TEST enabled. This makes it easier to build-test them. Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20250624-pwrseq-match-defines-v1-2-a59d90a951f1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig index 0f118d57c1ced..280f92beb5d0e 100644 --- a/drivers/power/sequencing/Kconfig +++ b/drivers/power/sequencing/Kconfig @@ -16,7 +16,7 @@ if POWER_SEQUENCING config POWER_SEQUENCING_QCOM_WCN tristate "Qualcomm WCN family PMU driver" default m if ARCH_QCOM - depends on OF + depends on OF || COMPILE_TEST help Say Y here to enable the power sequencing driver for Qualcomm WCN Bluetooth/WLAN chipsets. @@ -29,7 +29,7 @@ config POWER_SEQUENCING_QCOM_WCN config POWER_SEQUENCING_TH1520_GPU tristate "T-HEAD TH1520 GPU power sequencing driver" - depends on ARCH_THEAD && AUXILIARY_BUS + depends on (ARCH_THEAD && AUXILIARY_BUS) || COMPILE_TEST help Say Y here to enable the power sequencing driver for the TH1520 SoC GPU. This driver handles the complex clock and reset sequence -- GitLab From 62b5848f73dd4f8ae17304dae54562d0c9ecdd3d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 24 Jun 2025 16:32:20 +0200 Subject: [PATCH 305/868] power: sequencing: add defines for return values of the match() callback Instead of using 0 and 1 as magic numbers, let's add proper defines whose names tell the reader what the meaning behind them is. Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20250624-pwrseq-match-defines-v1-3-a59d90a951f1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/core.c | 6 +++--- include/linux/pwrseq/provider.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/power/sequencing/core.c b/drivers/power/sequencing/core.c index 0ffc259c6bb6c..190564e559885 100644 --- a/drivers/power/sequencing/core.c +++ b/drivers/power/sequencing/core.c @@ -628,7 +628,7 @@ static int pwrseq_match_device(struct device *pwrseq_dev, void *data) return 0; ret = pwrseq->match(pwrseq, match_data->dev); - if (ret <= 0) + if (ret == PWRSEQ_NO_MATCH || ret < 0) return ret; /* We got the matching device, let's find the right target. */ @@ -651,7 +651,7 @@ static int pwrseq_match_device(struct device *pwrseq_dev, void *data) match_data->desc->pwrseq = pwrseq_device_get(pwrseq); - return 1; + return PWRSEQ_MATCH_OK; } /** @@ -684,7 +684,7 @@ struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target) pwrseq_match_device); if (ret < 0) return ERR_PTR(ret); - if (ret == 0) + if (ret == PWRSEQ_NO_MATCH) /* No device matched. */ return ERR_PTR(-EPROBE_DEFER); diff --git a/include/linux/pwrseq/provider.h b/include/linux/pwrseq/provider.h index cbc3607cbfcfe..33b3d2c2e39de 100644 --- a/include/linux/pwrseq/provider.h +++ b/include/linux/pwrseq/provider.h @@ -13,6 +13,9 @@ struct pwrseq_device; typedef int (*pwrseq_power_state_func)(struct pwrseq_device *); typedef int (*pwrseq_match_func)(struct pwrseq_device *, struct device *); +#define PWRSEQ_NO_MATCH 0 +#define PWRSEQ_MATCH_OK 1 + /** * struct pwrseq_unit_data - Configuration of a single power sequencing * unit. -- GitLab From f698155029efc708349126c8944fa8c95b28098c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 24 Jun 2025 16:32:21 +0200 Subject: [PATCH 306/868] power: sequencing: qcom-wcn: use new defines for match() return values Replace the magic numbers with proper defines we now have in the header. Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20250624-pwrseq-match-defines-v1-4-a59d90a951f1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-qcom-wcn.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-qcom-wcn.c b/drivers/power/sequencing/pwrseq-qcom-wcn.c index e8f5030f2639a..f14801b4c28e5 100644 --- a/drivers/power/sequencing/pwrseq-qcom-wcn.c +++ b/drivers/power/sequencing/pwrseq-qcom-wcn.c @@ -341,12 +341,12 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, * device. */ if (!of_property_present(dev_node, "vddaon-supply")) - return 0; + return PWRSEQ_NO_MATCH; struct device_node *reg_node __free(device_node) = of_parse_phandle(dev_node, "vddaon-supply", 0); if (!reg_node) - return 0; + return PWRSEQ_NO_MATCH; /* * `reg_node` is the PMU AON regulator, its parent is the `regulators` @@ -355,9 +355,9 @@ static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq, */ if (!reg_node->parent || !reg_node->parent->parent || reg_node->parent->parent != ctx->of_node) - return 0; + return PWRSEQ_NO_MATCH; - return 1; + return PWRSEQ_MATCH_OK; } static int pwrseq_qcom_wcn_probe(struct platform_device *pdev) -- GitLab From 385b735c90ae44dbde65fab76e356a96ff8f67be Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 24 Jun 2025 16:32:22 +0200 Subject: [PATCH 307/868] power: sequencing: thead-gpu: use new defines for match() return values Replace the magic numbers with proper defines we now have in the header. Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20250624-pwrseq-match-defines-v1-5-a59d90a951f1@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-thead-gpu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/power/sequencing/pwrseq-thead-gpu.c b/drivers/power/sequencing/pwrseq-thead-gpu.c index 855c6cc4f3b5b..7c82a10ca9f69 100644 --- a/drivers/power/sequencing/pwrseq-thead-gpu.c +++ b/drivers/power/sequencing/pwrseq-thead-gpu.c @@ -124,24 +124,25 @@ static int pwrseq_thead_gpu_match(struct pwrseq_device *pwrseq, /* We only match the specific T-HEAD TH1520 GPU compatible */ if (!of_device_is_compatible(dev->of_node, "thead,th1520-gpu")) - return 0; + return PWRSEQ_NO_MATCH; ret = of_parse_phandle_with_args(dev->of_node, "power-domains", "#power-domain-cells", 0, &pwr_spec); if (ret) - return 0; + return PWRSEQ_NO_MATCH; /* Additionally verify consumer device has AON as power-domain */ if (pwr_spec.np != ctx->aon_node || pwr_spec.args[0] != TH1520_GPU_PD) { of_node_put(pwr_spec.np); - return 0; + return PWRSEQ_NO_MATCH; } of_node_put(pwr_spec.np); /* If a consumer is already bound, only allow a re-match from it */ if (ctx->consumer_node) - return ctx->consumer_node == dev->of_node ? 1 : 0; + return ctx->consumer_node == dev->of_node ? + PWRSEQ_MATCH_OK : PWRSEQ_NO_MATCH; ctx->num_clks = ARRAY_SIZE(clk_names); ctx->clks = kcalloc(ctx->num_clks, sizeof(*ctx->clks), GFP_KERNEL); @@ -163,7 +164,7 @@ static int pwrseq_thead_gpu_match(struct pwrseq_device *pwrseq, ctx->consumer_node = of_node_get(dev->of_node); - return 1; + return PWRSEQ_MATCH_OK; err_put_clks: clk_bulk_put(ctx->num_clks, ctx->clks); -- GitLab From 07d59dec6795428983a840de85aa02febaf7e01b Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 25 Jun 2025 17:55:43 +0200 Subject: [PATCH 308/868] power: sequencing: qcom-wcn: fix bluetooth-wifi copypasta for WCN6855 Prevent a name conflict (which is surprisingly not caught by the framework). Fixes: bd4c8bafcf50 ("power: sequencing: qcom-wcn: improve support for wcn6855") Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20250625-topic-wcn6855_pwrseq-v1-1-cfb96d599ff8@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/power/sequencing/pwrseq-qcom-wcn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/sequencing/pwrseq-qcom-wcn.c b/drivers/power/sequencing/pwrseq-qcom-wcn.c index f14801b4c28e5..663d9a5370653 100644 --- a/drivers/power/sequencing/pwrseq-qcom-wcn.c +++ b/drivers/power/sequencing/pwrseq-qcom-wcn.c @@ -155,7 +155,7 @@ static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = { }; static const struct pwrseq_unit_data pwrseq_qcom_wcn6855_bt_unit_data = { - .name = "wlan-enable", + .name = "bluetooth-enable", .deps = pwrseq_qcom_wcn6855_unit_deps, .enable = pwrseq_qcom_wcn_bt_enable, .disable = pwrseq_qcom_wcn_bt_disable, -- GitLab From a8fc1224f2318d3e5948671d1cad458e6372d921 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 9 Jun 2025 12:46:19 +0200 Subject: [PATCH 309/868] platform/x86: x86-android-tablets: Add generic_lipo_4v2_battery info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the asus_tf103c_battery_node to shared-psy-info.c and rename it to generic_lipo_4v2_battery_node. This is a preparation patch for adding ovc-capacity-table info to the battery nodes. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20250609104620.25896-1-hansg@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/x86-android-tablets/asus.c | 21 +++---------------- .../x86/x86-android-tablets/shared-psy-info.c | 16 ++++++++++++++ .../x86/x86-android-tablets/shared-psy-info.h | 1 + 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c index 7dde63b9943f1..97cd14c1fd23c 100644 --- a/drivers/platform/x86/x86-android-tablets/asus.c +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -206,24 +206,9 @@ static const struct software_node asus_tf103c_touchscreen_node = { .properties = asus_tf103c_touchscreen_props, }; -static const struct property_entry asus_tf103c_battery_props[] = { - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), - { } -}; - -static const struct software_node asus_tf103c_battery_node = { - .properties = asus_tf103c_battery_props, -}; - static const struct property_entry asus_tf103c_bq24190_props[] = { PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node), PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), PROPERTY_ENTRY_BOOL("omit-battery-class"), PROPERTY_ENTRY_BOOL("disable-reset"), @@ -236,7 +221,7 @@ static const struct software_node asus_tf103c_bq24190_node = { static const struct property_entry asus_tf103c_ug3105_props[] = { PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_4v2_battery_node), PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), { } }; @@ -321,6 +306,6 @@ const struct x86_dev_info asus_tf103c_info __initconst = { .gpio_button = &asus_me176c_tf103c_lid, .gpio_button_count = 1, .gpiod_lookup_tables = asus_tf103c_gpios, - .bat_swnode = &asus_tf103c_battery_node, + .bat_swnode = &generic_lipo_4v2_battery_node, .modules = bq24190_modules, }; diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c index a46fa15acfb14..55da574771534 100644 --- a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c @@ -39,6 +39,22 @@ const struct software_node fg_bq25890_supply_node = { .properties = fg_bq25890_supply_props, }; +/* Standard LiPo (max 4.2V) settings used by most devs with a LiPo battery */ +static const struct property_entry generic_lipo_4v2_battery_props[] = { + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + { } +}; + +const struct software_node generic_lipo_4v2_battery_node = { + .properties = generic_lipo_4v2_battery_props, +}; + /* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV battery */ static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { PROPERTY_ENTRY_STRING("compatible", "simple-battery"), diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.h b/drivers/platform/x86/x86-android-tablets/shared-psy-info.h index c2d2968cddc2d..bcf9845ad275c 100644 --- a/drivers/platform/x86/x86-android-tablets/shared-psy-info.h +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.h @@ -21,6 +21,7 @@ extern const char * const bq25890_psy[]; extern const struct software_node fg_bq24190_supply_node; extern const struct software_node fg_bq25890_supply_node; +extern const struct software_node generic_lipo_4v2_battery_node; extern const struct software_node generic_lipo_hv_4v35_battery_node; extern struct bq24190_platform_data bq24190_pdata; -- GitLab From be91bf40a96d567973d5c5e870d1464eb51b6c42 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 9 Jun 2025 12:46:20 +0200 Subject: [PATCH 310/868] platform/x86: x86-android-tablets: Add ovc-capacity-table info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ovc-capacity-table info to the generic battery nodes. The values come from the ug3105 driver which currently hardcodes these values. The ug3105 driver will be modified to stop hardcoding this and instead get the values from device-properties. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20250609104620.25896-2-hansg@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/x86-android-tablets/shared-psy-info.c | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c index 55da574771534..fe34cedb6257b 100644 --- a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c @@ -39,6 +39,58 @@ const struct software_node fg_bq25890_supply_node = { .properties = fg_bq25890_supply_props, }; +static const u32 generic_lipo_battery_ovc_cap_celcius[] = { 25 }; + +static const u32 generic_lipo_4v2_battery_ovc_cap_table0[] = { + 4200000, 100, + 4150000, 95, + 4110000, 90, + 4075000, 85, + 4020000, 80, + 3982500, 75, + 3945000, 70, + 3907500, 65, + 3870000, 60, + 3853333, 55, + 3836667, 50, + 3820000, 45, + 3803333, 40, + 3786667, 35, + 3770000, 30, + 3750000, 25, + 3730000, 20, + 3710000, 15, + 3690000, 10, + 3610000, 5, + 3350000, 0 +}; + +static const u32 generic_lipo_hv_4v35_battery_ovc_cap_table0[] = { + 4300000, 100, + 4250000, 96, + 4200000, 91, + 4150000, 86, + 4110000, 82, + 4075000, 77, + 4020000, 73, + 3982500, 68, + 3945000, 64, + 3907500, 59, + 3870000, 55, + 3853333, 50, + 3836667, 45, + 3820000, 41, + 3803333, 36, + 3786667, 32, + 3770000, 27, + 3750000, 23, + 3730000, 18, + 3710000, 14, + 3690000, 9, + 3610000, 5, + 3350000, 0 +}; + /* Standard LiPo (max 4.2V) settings used by most devs with a LiPo battery */ static const struct property_entry generic_lipo_4v2_battery_props[] = { PROPERTY_ENTRY_STRING("compatible", "simple-battery"), @@ -48,6 +100,10 @@ static const struct property_entry generic_lipo_4v2_battery_props[] = { PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius", + generic_lipo_battery_ovc_cap_celcius), + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0", + generic_lipo_4v2_battery_ovc_cap_table0), { } }; @@ -64,6 +120,10 @@ static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-celsius", + generic_lipo_battery_ovc_cap_celcius), + PROPERTY_ENTRY_U32_ARRAY("ocv-capacity-table-0", + generic_lipo_hv_4v35_battery_ovc_cap_table0), { } }; -- GitLab From bd7c7976f9716da4cc961ab8ff4e17811d522602 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:48:03 +0800 Subject: [PATCH 311/868] regulator: rt5739: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629094803.776-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/rt5739.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/rt5739.c b/drivers/regulator/rt5739.c index 91412c905ce64..5fcddd7c2da73 100644 --- a/drivers/regulator/rt5739.c +++ b/drivers/regulator/rt5739.c @@ -24,6 +24,8 @@ #define RT5739_REG_NSEL1 0x01 #define RT5739_REG_CNTL1 0x02 #define RT5739_REG_ID1 0x03 +#define RT5739_REG_ID2 0x04 +#define RT5739_REG_MON 0x05 #define RT5739_REG_CNTL2 0x06 #define RT5739_REG_CNTL4 0x08 @@ -236,11 +238,18 @@ static void rt5739_init_regulator_desc(struct regulator_desc *desc, } } +static bool rt5739_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == RT5739_REG_MON; +} + static const struct regmap_config rt5739_regmap_config = { .name = "rt5739", .reg_bits = 8, .val_bits = 8, .max_register = RT5739_REG_CNTL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = rt5739_volatile_reg, }; static int rt5739_probe(struct i2c_client *i2c) -- GitLab From b402dfe84057e376b39c8adadeb65ad51aaffeb4 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:58:22 +0800 Subject: [PATCH 312/868] regulator: tps6287x-regulator: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629095822.868-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6287x-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/tps6287x-regulator.c b/drivers/regulator/tps6287x-regulator.c index c0f5f0a186a3d..7b7d3ae392065 100644 --- a/drivers/regulator/tps6287x-regulator.c +++ b/drivers/regulator/tps6287x-regulator.c @@ -27,10 +27,17 @@ #define TPS6287X_CTRL3 0x03 #define TPS6287X_STATUS 0x04 +static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6287X_STATUS; +} + static const struct regmap_config tps6287x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = TPS6287X_STATUS, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6287x_volatile_reg, }; static const struct linear_range tps6287x_voltage_ranges[] = { -- GitLab From 427ceac823e58813b510e585011488f603f0d891 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:51:07 +0800 Subject: [PATCH 313/868] regulator: tps6286x-regulator: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629095107.804-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6286x-regulator.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c index 75f441f36de7c..778f169b0acc6 100644 --- a/drivers/regulator/tps6286x-regulator.c +++ b/drivers/regulator/tps6286x-regulator.c @@ -19,13 +19,22 @@ #define TPS6286X_CONTROL_FPWM BIT(4) #define TPS6286X_CONTROL_SWEN BIT(5) +#define TPS6286X_STATUS 0x05 + #define TPS6286X_MIN_MV 400 #define TPS6286X_MAX_MV 1675 #define TPS6286X_STEP_MV 5 +static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6286X_STATUS; +} + static const struct regmap_config tps6286x_regmap_config = { .reg_bits = 8, .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6287x_volatile_reg, }; static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) -- GitLab From 7e1c28fbf235791cb5046fafdac5bc16fe8e788d Mon Sep 17 00:00:00 2001 From: Thangaraj Samynathan Date: Mon, 30 Jun 2025 13:02:33 +0530 Subject: [PATCH 314/868] spi: spi-pci1xxxx: enable concurrent DMA read/write across SPI transfers Refactor the pci1xxxx SPI driver to allow overlapping DMA read and write operations across SPI transfers. This improves throughput and reduces idle time between SPI transactions. Transfer sequence: - Start with a DMA read to load TX data from host to device buffer. - After DMA read completes, trigger the SPI transfer. - On SPI completion: - Start DMA write to copy received data from RX buffer to host. - Start the next DMA read to prepare TX data for the following transfer. - Begin the next SPI transfer after both DMA write and read complete. To implement this sequence, the following changes were made: - Added dma_completion_count to track and synchronize DMA completions. - Split DMA setup into separate functions for TX (read) and RX (write). - Introduced separate spinlocks for safe access to RD and WR DMA registers. This new flow enables efficient pipelining by overlapping data preparation and completion stages, leading to better SPI transfer performance and utilization of DMA engines. Signed-off-by: Thangaraj Samynathan Link: https://patch.msgid.link/20250630073233.7356-1-thangaraj.s@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 82 ++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index f44fe58411392..8577a19705de4 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -140,6 +140,7 @@ struct pci1xxxx_spi_internal { int irq[NUM_VEC_PER_INST]; int mode; bool spi_xfer_in_progress; + atomic_t dma_completion_count; void *rx_buf; bool dma_aborted_rd; u32 bytes_recvd; @@ -163,8 +164,10 @@ struct pci1xxxx_spi { u8 dev_rev; void __iomem *reg_base; void __iomem *dma_offset_bar; - /* lock to safely access the DMA registers in isr */ - spinlock_t dma_reg_lock; + /* lock to safely access the DMA RD registers in isr */ + spinlock_t dma_rd_reg_lock; + /* lock to safely access the DMA RD registers in isr */ + spinlock_t dma_wr_reg_lock; bool can_dma; struct pci1xxxx_spi_internal *spi_int[] __counted_by(total_hw_instances); }; @@ -330,7 +333,8 @@ static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int if (ret) return ret; - spin_lock_init(&spi_bus->dma_reg_lock); + spin_lock_init(&spi_bus->dma_rd_reg_lock); + spin_lock_init(&spi_bus->dma_wr_reg_lock); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN); writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN); @@ -464,6 +468,7 @@ static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p) { u32 regval; + atomic_set(&p->dma_completion_count, 0); regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); regval |= SPI_MST_CTL_GO; writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); @@ -536,7 +541,6 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, { struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr); struct pci1xxxx_spi *par = p->parent; - dma_addr_t rx_dma_addr = 0; dma_addr_t tx_dma_addr = 0; int ret = 0; u32 regval; @@ -545,6 +549,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, p->tx_sgl = xfer->tx_sg.sgl; p->rx_sgl = xfer->rx_sg.sgl; p->rx_buf = xfer->rx_buf; + atomic_set(&p->dma_completion_count, 1); regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); @@ -561,13 +566,9 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr, writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); tx_dma_addr = sg_dma_address(p->tx_sgl); - rx_dma_addr = sg_dma_address(p->rx_sgl); p->tx_sgl_len = sg_dma_len(p->tx_sgl); - p->rx_sgl_len = sg_dma_len(p->rx_sgl); pci1xxxx_spi_setup(par, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len); pci1xxxx_spi_setup_dma_to_io(p, (tx_dma_addr), p->tx_sgl_len); - if (rx_dma_addr) - pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); writel(p->hw_inst, par->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); reinit_completion(&p->spi_xfer_done); @@ -657,32 +658,33 @@ static irqreturn_t pci1xxxx_spi_isr_io(int irq, void *dev) return spi_int_fired; } -static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p) +static void pci1xxxx_spi_setup_next_dma_to_io_transfer(struct pci1xxxx_spi_internal *p) { dma_addr_t tx_dma_addr = 0; - dma_addr_t rx_dma_addr = 0; u32 prev_len; p->tx_sgl = sg_next(p->tx_sgl); - if (p->rx_sgl) - p->rx_sgl = sg_next(p->rx_sgl); - if (!p->tx_sgl) { - /* Clear xfer_done */ - complete(&p->spi_xfer_done); - } else { + if (p->tx_sgl) { tx_dma_addr = sg_dma_address(p->tx_sgl); prev_len = p->tx_sgl_len; p->tx_sgl_len = sg_dma_len(p->tx_sgl); + pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len); + writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); if (prev_len != p->tx_sgl_len) pci1xxxx_spi_setup(p->parent, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len); - pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len); - if (p->rx_sgl) { - rx_dma_addr = sg_dma_address(p->rx_sgl); - p->rx_sgl_len = sg_dma_len(p->rx_sgl); - pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); - } - writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG); + } +} + +static void pci1xxxx_spi_setup_next_dma_from_io_transfer(struct pci1xxxx_spi_internal *p) +{ + dma_addr_t rx_dma_addr = 0; + + if (p->rx_sgl) { + rx_dma_addr = sg_dma_address(p->rx_sgl); + p->rx_sgl_len = sg_dma_len(p->rx_sgl); + pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len); + writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG); } } @@ -693,22 +695,24 @@ static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev) unsigned long flags; u32 regval; - spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA RD INT and start spi xfer*/ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS); if (regval) { if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { - pci1xxxx_start_spi_xfer(p); + /* Start the SPI transfer only if both DMA read and write are completed */ + if (atomic_inc_return(&p->dma_completion_count) == 2) + pci1xxxx_start_spi_xfer(p); spi_int_fired = IRQ_HANDLED; } if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { p->dma_aborted_rd = true; spi_int_fired = IRQ_HANDLED; } + spin_lock_irqsave(&p->parent->dma_rd_reg_lock, flags); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); + spin_unlock_irqrestore(&p->parent->dma_rd_reg_lock, flags); } - writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), - p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR); - spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); return spi_int_fired; } @@ -719,22 +723,29 @@ static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev) unsigned long flags; u32 regval; - spin_lock_irqsave(&p->parent->dma_reg_lock, flags); /* Clear the DMA WR INT */ regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS); if (regval) { if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) { - pci1xxxx_spi_setup_next_dma_transfer(p); spi_int_fired = IRQ_HANDLED; + if (sg_is_last(p->rx_sgl)) { + complete(&p->spi_xfer_done); + } else { + p->rx_sgl = sg_next(p->rx_sgl); + if (atomic_inc_return(&p->dma_completion_count) == 2) + pci1xxxx_start_spi_xfer(p); + } + } if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) { p->dma_aborted_wr = true; spi_int_fired = IRQ_HANDLED; } + spin_lock_irqsave(&p->parent->dma_wr_reg_lock, flags); + writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), + p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); + spin_unlock_irqrestore(&p->parent->dma_wr_reg_lock, flags); } - writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)), - p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR); - spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags); return spi_int_fired; } @@ -747,10 +758,11 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev) /* Clear the SPI GO_BIT Interrupt */ regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); if (regval & SPI_INTR) { - writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG); + pci1xxxx_spi_setup_next_dma_from_io_transfer(p); + pci1xxxx_spi_setup_next_dma_to_io_transfer(p); spi_int_fired = IRQ_HANDLED; + writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); } - writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); return spi_int_fired; } -- GitLab From 90c8c31e19d4fe659dc38e537f241e79266ff478 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 30 Jun 2025 12:54:18 +0200 Subject: [PATCH 315/868] ALSA: timer: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250630105420.1448-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/timer.c b/sound/core/timer.c index 8072183c33d39..3ce12264eed8a 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1195,7 +1195,7 @@ static int snd_timer_register_system(void) err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); if (err < 0) return err; - strcpy(timer->name, "system timer"); + strscpy(timer->name, "system timer"); timer->hw = snd_timer_system; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) { -- GitLab From 24ffcf7f27cf75389ca550a18f9e45a8dad27bd2 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 30 Jun 2025 12:57:22 +0200 Subject: [PATCH 316/868] ALSA: hrtimer: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250630105723.1703-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/core/hrtimer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index e9c60dce59fbf..c364bd126ac8b 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -138,7 +139,7 @@ static int __init snd_hrtimer_init(void) return err; timer->module = THIS_MODULE; - strcpy(timer->name, "HR timer"); + strscpy(timer->name, "HR timer"); timer->hw = hrtimer_hw; timer->hw.resolution = resolution; timer->hw.ticks = NANO_SEC / resolution; -- GitLab From 66b338d006d75ab52b16bf05a7f4f451043199bf Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 30 Jun 2025 13:09:44 +0200 Subject: [PATCH 317/868] ALSA: dummy: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250630110945.2225-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/drivers/dummy.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 783fe3a22bc91..6dac0b2523c11 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -684,7 +685,7 @@ static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); pcm->private_data = dummy; pcm->info_flags = 0; - strcpy(pcm->name, "Dummy PCM"); + strscpy(pcm->name, "Dummy PCM"); if (!fake_buffer) { snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, @@ -875,7 +876,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy) int err; spin_lock_init(&dummy->mixer_lock); - strcpy(card->mixername, "Dummy Mixer"); + strscpy(card->mixername, "Dummy Mixer"); dummy->iobox = 1; for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { @@ -1083,8 +1084,8 @@ static int snd_dummy_probe(struct platform_device *devptr) err = snd_card_dummy_new_mixer(dummy); if (err < 0) return err; - strcpy(card->driver, "Dummy"); - strcpy(card->shortname, "Dummy"); + strscpy(card->driver, "Dummy"); + strscpy(card->shortname, "Dummy"); sprintf(card->longname, "Dummy %i", dev + 1); dummy_proc_init(dummy); -- GitLab From f4d8438e6a402ad40cf4ccb6e2d2417d9ed47821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Le=20Goffic?= Date: Mon, 30 Jun 2025 14:59:23 +0200 Subject: [PATCH 318/868] spi: stm32: fix sram pool free in probe error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test to check whether the sram_pool is NULL before freeing it. Fixes: d17dd2f1d8a1 ("spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use") Reported-by: Dan Carpenter Acked-by: Alain Volmat Signed-off-by: Clément Le Goffic Link: https://patch.msgid.link/20250630-spi-fix-v2-1-4680939e2a3e@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 3d20f09f1ae7e..858470a2cab52 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2486,7 +2486,9 @@ err_pm_disable: if (spi->mdma_rx) dma_release_channel(spi->mdma_rx); err_pool_free: - gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, spi->sram_rx_buf_size); + if (spi->sram_pool) + gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf, + spi->sram_rx_buf_size); err_dma_release: if (spi->dma_tx) dma_release_channel(spi->dma_tx); -- GitLab From c4f2c05ab02952c9a56067aeb700ded95b183570 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 30 Jun 2025 10:12:53 +0200 Subject: [PATCH 319/868] spi: stm32: fix pointer-to-pointer variables usage In stm32_spi_prepare_rx_dma_mdma_chaining() both rx_dma_desc and rx_mdma_desc are passed as pointer-to-pointer arguments. The goal is to pass back to the caller the value returned by dmaengine_prep_slave_sg(), when it is not NULL. However, these variables are wrongly handled as simple pointers during later assignments and checks. Fix this behaviour by introducing two pointer variables which can then be treated accordingly. Fixes: d17dd2f1d8a1 ("spi: stm32: use STM32 DMA with STM32 MDMA to enhance DDR use") Addresses-Coverity-ID: 1644715 ("Null pointer dereferences (REVERSE_INULL)") Signed-off-by: Antonio Quartulli Reviewed-by: Clement LE GOFFIC Link: https://patch.msgid.link/20250630081253.17294-1-antonio@mandelbit.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 858470a2cab52..9b3bc0c908bf2 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1474,6 +1474,8 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, struct dma_async_tx_descriptor **rx_dma_desc, struct dma_async_tx_descriptor **rx_mdma_desc) { + struct dma_async_tx_descriptor *_mdma_desc = *rx_mdma_desc; + struct dma_async_tx_descriptor *_dma_desc = *rx_dma_desc; struct dma_slave_config rx_mdma_conf = {0}; u32 sram_period, nents = 0, spi_s_len; struct sg_table dma_sgt, mdma_sgt; @@ -1524,18 +1526,18 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, } } - *rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, - dma_sgt.nents, rx_dma_conf->direction, - DMA_PREP_INTERRUPT); + _dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl, + dma_sgt.nents, rx_dma_conf->direction, + DMA_PREP_INTERRUPT); sg_free_table(&dma_sgt); - if (!rx_dma_desc) + if (!_dma_desc) return -EINVAL; /* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */ ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC); if (ret) { - rx_dma_desc = NULL; + _dma_desc = NULL; return ret; } @@ -1558,13 +1560,13 @@ static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi, } } - *rx_mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, - mdma_sgt.nents, rx_mdma_conf.direction, - DMA_PREP_INTERRUPT); + _mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl, + mdma_sgt.nents, rx_mdma_conf.direction, + DMA_PREP_INTERRUPT); sg_free_table(&mdma_sgt); - if (!rx_mdma_desc) { - rx_dma_desc = NULL; + if (!_mdma_desc) { + _dma_desc = NULL; return -EINVAL; } -- GitLab From 0383a710d28dd5dc43d26ccf9fc74e1ba41868fd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:38 +0100 Subject: [PATCH 320/868] MAINTAINERS: Add SDCA maintainers entry Add a maintainers entry for the new SDCA support code. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- MAINTAINERS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163..b9782f84bb369 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22298,6 +22298,17 @@ M: Jim Cromie S: Maintained F: drivers/clocksource/scx200_hrt.c +SDCA LIBRARY AND CLASS DRIVER +M: Charles Keepax +M: Maciej Strozek +R: Bard Liao +R: Pierre-Louis Bossart +L: linux-sound@vger.kernel.org +L: patches@opensource.cirrus.com +S: Maintained +F: include/sound/sdca* +F: sound/soc/sdca/* + SDRICOH_CS MMC/SD HOST CONTROLLER INTERFACE DRIVER M: Sascha Sommer L: sdricohcs-devel@lists.sourceforge.net (subscribers-only) -- GitLab From 2ed526bf04a6d81592b314f81e7719a14048f732 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:39 +0100 Subject: [PATCH 321/868] ASoC: SDCA: Add missing default in switch in entity_pde_event() The current code should be safe as the PDE widget only registers for the two events handled in the switch statement. However, it is causing a smatch warning and also is a little fragile to future code changes, add a default case to avoid the warning and make the code more robust. Fixes: 2c8b3a8e6aa8 ("ASoC: SDCA: Create DAPM widgets and routes from DisCo") Reported-by: Dan Carpenter Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 7bc8f6069f3d4..e96e696cb1079 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -397,6 +397,8 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget, from = widget->off_val; to = widget->on_val; break; + default: + return 0; } for (i = 0; i < entity->pde.num_max_delay; i++) { -- GitLab From b4515fd87cc9a260ae89fb81ca2aa928d496ac86 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:40 +0100 Subject: [PATCH 322/868] ASoC: SDCA: Fixup some kernel doc errors Correct some typos and omissions in the kernel doc for the ASoC SDCA code. Fixes: 2c8b3a8e6aa8 ("ASoC: SDCA: Create DAPM widgets and routes from DisCo") Reported-by: Bard Liao Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index e96e696cb1079..83911dab73ae2 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -93,6 +93,7 @@ static bool readonly_control(struct sdca_control *control) /** * sdca_asoc_count_component - count the various component parts + * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. * @num_widgets: Output integer pointer, will be filled with the * required number of DAPM widgets for the Function. @@ -997,7 +998,7 @@ static int populate_pin_switch(struct device *dev, * sdca_asoc_populate_controls - fill in an array of ALSA controls for a Function * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. - * @route: Array of ALSA controls to be populated. + * @kctl: Array of ALSA controls to be populated. * * This function populates an array of ALSA controls from the DisCo * information for a particular SDCA Function. Typically, @@ -1244,7 +1245,11 @@ EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA"); * sdca_asoc_populate_component - fill in a component driver for a Function * @dev: Pointer to the device against which allocations will be done. * @function: Pointer to the Function information. - * @copmonent_drv: Pointer to the component driver to be populated. + * @component_drv: Pointer to the component driver to be populated. + * @dai_drv: Pointer to the DAI driver array to be allocated and populated. + * @num_dai_drv: Pointer to integer that will be populated with the number of + * DAI drivers. + * @ops: DAI ops pointer that will be used for each DAI driver. * * This function populates a snd_soc_component_driver structure based * on the DisCo information for a particular SDCA Function. It does -- GitLab From 37d2aa62138daa8ecb6442ae4753704e9c92346f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:41 +0100 Subject: [PATCH 323/868] ASoC: SDCA: Minor selected/detected mode control fixups Make the names a slightly better match for the specification and add some constants for the values rather than hard coding. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 9 +++++++++ sound/soc/sdca/sdca_asoc.c | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 856b0f40ce5eb..4b278513597e5 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -319,6 +319,15 @@ enum sdca_selected_mode_range { SDCA_SELECTED_MODE_NCOLS = 2, }; +/** + * enum sdca_detected_mode_values - Predefined GE Detected Mode values + */ +enum sdca_detected_mode_values { + SDCA_DETECTED_MODE_JACK_UNPLUGGED = 0, + SDCA_DETECTED_MODE_JACK_UNKNOWN = 1, + SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS = 2, +}; + /** * enum sdca_spe_controls - SDCA Controls for Security & Privacy Unit * diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 83911dab73ae2..dd7b19083c85f 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -246,12 +246,12 @@ static int entity_early_parse_ge(struct device *dev, if (!values) return -ENOMEM; - texts[0] = "No Jack"; + texts[0] = "Jack Unplugged"; texts[1] = "Jack Unknown"; texts[2] = "Detection in Progress"; - values[0] = 0; - values[1] = 1; - values[2] = 2; + values[0] = SDCA_DETECTED_MODE_JACK_UNPLUGGED; + values[1] = SDCA_DETECTED_MODE_JACK_UNKNOWN; + values[2] = SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS; for (i = 0; i < range->rows; i++) { enum sdca_terminal_type type; -- GitLab From 775f5729b47d8737f4f98e0141f61b3358245398 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:42 +0100 Subject: [PATCH 324/868] ASoC: SDCA: Add flag for unused IRQs Zero is a valid SDCA IRQ interrupt position so add a special value to indicate that the IRQ is not used. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 2 ++ sound/soc/sdca/sdca_functions.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 4b278513597e5..b4a97ff087294 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -17,6 +17,8 @@ struct device; struct sdca_entity; struct sdca_function_desc; +#define SDCA_NO_INTERRUPT -1 + /* * The addressing space for SDCA relies on 7 bits for Entities, so a * maximum of 128 Entities per function can be represented. diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 093c681e93879..c34f3bf629833 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -912,6 +912,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti &tmp); if (!ret) control->interrupt_position = tmp; + else + control->interrupt_position = SDCA_NO_INTERRUPT; control->label = find_sdca_control_label(dev, entity, control); if (!control->label) -- GitLab From b126394d9ec6f9d8322cf392ba23d4a5f96faf5a Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Tue, 24 Jun 2025 13:28:43 +0100 Subject: [PATCH 325/868] ASoC: SDCA: Generic interrupt support Add a library supporting usage of SDCA interrupts, using regmap irq framework. The library adds functions for parsing ACPI for interrupt-related information, configuring irq chip and requesting individual irqs. Calling code (SDCA function code) is expected to also substitute the library's base irq handler for its own, appropriate callback. Signed-off-by: Maciej Strozek Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 75 ++++++++ sound/soc/sdca/Kconfig | 7 + sound/soc/sdca/Makefile | 5 +- sound/soc/sdca/sdca_interrupts.c | 284 +++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 include/sound/sdca_interrupts.h create mode 100644 sound/soc/sdca/sdca_interrupts.c diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h new file mode 100644 index 0000000000000..4cda8b75bae0e --- /dev/null +++ b/include/sound/sdca_interrupts.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + * + * Copyright (C) 2025 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __SDCA_INTERRUPTS_H__ +#define __SDCA_INTERRUPTS_H__ + +#include +#include +#include + +struct device; +struct snd_soc_component; +struct sdca_function_data; + +#define SDCA_MAX_INTERRUPTS 31 /* the last bit is reserved for future extensions */ + +/** + * struct sdca_interrupt - contains information about a single SDCA interrupt + * @name: The name of the interrupt. + * @component: Pointer to the ASoC component owns the interrupt. + * @function: Pointer to the Function that the interrupt is associated with. + * @entity: Pointer to the Entity that the interrupt is associated with. + * @control: Pointer to the Control that the interrupt is associated with. + * @externally_requested: Internal flag used to check if a client driver has + * already requested the interrupt, for custom handling, allowing the core to + * skip handling this interrupt. + */ +struct sdca_interrupt { + const char *name; + + struct snd_soc_component *component; + struct sdca_function_data *function; + struct sdca_entity *entity; + struct sdca_control *control; + + bool externally_requested; +}; + +/** + * struct sdca_interrupt_info - contains top-level SDCA interrupt information + * @irq_chip: regmap irq chip structure. + * @irq_data: regmap irq chip data structure. + * @irqs: Array of data for each individual IRQ. + * @irq_lock: Protects access to the list of sdca_interrupt structures. + */ +struct sdca_interrupt_info { + struct regmap_irq_chip irq_chip; + struct regmap_irq_chip_data *irq_data; + + struct sdca_interrupt irqs[SDCA_MAX_INTERRUPTS]; + + struct mutex irq_lock; /* Protect irqs list across functions */ +}; + +int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *interrupt_info, + int sdca_irq, const char *name, irq_handler_t handler, + void *data); +int sdca_irq_data_populate(struct snd_soc_component *component, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct sdca_control *control, + struct sdca_interrupt *interrupt); +int sdca_irq_populate(struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info); +struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, + struct regmap *regmap, int irq); + +#endif diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index ec28855fe3b09..a633d5a0fea51 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -13,3 +13,10 @@ config SND_SOC_SDCA_OPTIONAL config SND_SOC_SDCA_HID tristate "SDCA HID support" depends on SND_SOC_SDCA && HID + +config SND_SOC_SDCA_IRQ + tristate + select REGMAP + select REGMAP_IRQ + help + This option enables support for SDCA IRQs. diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 9af46e7edfd2a..2a3938d11ca90 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o - snd-soc-sdca-hid-y := sdca_hid.o +snd-soc-sdca-irq-y := sdca_interrupts.o -obj-$(CONFIG_SND_SOC_SDCA_HID) += snd-soc-sdca-hid.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o +obj-$(CONFIG_SND_SOC_SDCA_HID) += snd-soc-sdca-hid.o +obj-$(CONFIG_SND_SOC_SDCA_IRQ) += snd-soc-sdca-irq.o diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c new file mode 100644 index 0000000000000..7272d11cb6d49 --- /dev/null +++ b/sound/soc/sdca/sdca_interrupts.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \ + SDW_SCP_SDCA_INTMASK_SDCA_##number) + +static const struct regmap_irq regmap_irqs[SDCA_MAX_INTERRUPTS] = { + IRQ_SDCA(0), + IRQ_SDCA(1), + IRQ_SDCA(2), + IRQ_SDCA(3), + IRQ_SDCA(4), + IRQ_SDCA(5), + IRQ_SDCA(6), + IRQ_SDCA(7), + IRQ_SDCA(8), + IRQ_SDCA(9), + IRQ_SDCA(10), + IRQ_SDCA(11), + IRQ_SDCA(12), + IRQ_SDCA(13), + IRQ_SDCA(14), + IRQ_SDCA(15), + IRQ_SDCA(16), + IRQ_SDCA(17), + IRQ_SDCA(18), + IRQ_SDCA(19), + IRQ_SDCA(20), + IRQ_SDCA(21), + IRQ_SDCA(22), + IRQ_SDCA(23), + IRQ_SDCA(24), + IRQ_SDCA(25), + IRQ_SDCA(26), + IRQ_SDCA(27), + IRQ_SDCA(28), + IRQ_SDCA(29), + IRQ_SDCA(30), +}; + +static const struct regmap_irq_chip sdca_irq_chip = { + .name = "sdca_irq", + + .status_base = SDW_SCP_SDCA_INT1, + .unmask_base = SDW_SCP_SDCA_INTMASK1, + .ack_base = SDW_SCP_SDCA_INT1, + .num_regs = 4, + + .irqs = regmap_irqs, + .num_irqs = SDCA_MAX_INTERRUPTS, + + .runtime_pm = true, +}; + +static irqreturn_t base_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct device *dev = interrupt->component->dev; + + dev_info(dev, "%s irq without full handling\n", interrupt->name); + + return IRQ_HANDLED; +} + +static int sdca_irq_request_locked(struct device *dev, + struct sdca_interrupt_info *info, + int sdca_irq, const char *name, + irq_handler_t handler, void *data) +{ + int irq; + int ret; + + irq = regmap_irq_get_virq(info->irq_data, sdca_irq); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, handler, + IRQF_ONESHOT, name, data); + if (ret) + return ret; + + dev_dbg(dev, "requested irq %d for %s\n", irq, name); + + return 0; +} + +/** + * sdca_request_irq - request an individual SDCA interrupt + * @dev: Pointer to the struct device against which things should be allocated. + * @interrupt_info: Pointer to the interrupt information structure. + * @sdca_irq: SDCA interrupt position. + * @name: Name to be given to the IRQ. + * @handler: A callback thread function to be called for the IRQ. + * @data: Private data pointer that will be passed to the handler. + * + * Typically this is handled internally by sdca_irq_populate, however if + * a device requires custom IRQ handling this can be called manually before + * calling sdca_irq_populate, which will then skip that IRQ whilst processing. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, + int sdca_irq, const char *name, irq_handler_t handler, + void *data) +{ + int ret; + + if (sdca_irq < 0 || sdca_irq > SDCA_MAX_INTERRUPTS) { + dev_err(dev, "bad irq request: %d\n", sdca_irq); + return -EINVAL; + } + + guard(mutex)(&info->irq_lock); + + ret = sdca_irq_request_locked(dev, info, sdca_irq, name, handler, data); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", name, ret); + return ret; + } + + info->irqs[sdca_irq].externally_requested = true; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA_IRQ"); + +/** + * sdca_irq_data_populate - Populate common interrupt data + * @component: Pointer to the ASoC component for the Function. + * @function: Pointer to the SDCA Function. + * @entity: Pointer to the SDCA Entity. + * @control: Pointer to the SDCA Control. + * @interrupt: Pointer to the SDCA interrupt for this IRQ. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_data_populate(struct snd_soc_component *component, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct sdca_control *control, + struct sdca_interrupt *interrupt) +{ + struct device *dev = component->dev; + const char *name; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name, + entity->label, control->label); + if (!name) + return -ENOMEM; + + interrupt->name = name; + interrupt->component = component; + interrupt->function = function; + interrupt->entity = entity; + interrupt->control = control; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA_IRQ"); + +/** + * sdca_irq_populate - Request all the individual IRQs for an SDCA Function + * @function: Pointer to the SDCA Function. + * @component: Pointer to the ASoC component for the Function. + * @info: Pointer to the SDCA interrupt info for this device. + * + * Typically this would be called from the driver for a single SDCA Function. + * + * Return: Zero on success, and a negative error code on failure. + */ +int sdca_irq_populate(struct sdca_function_data *function, + struct snd_soc_component *component, + struct sdca_interrupt_info *info) +{ + struct device *dev = component->dev; + int i, j; + + guard(mutex)(&info->irq_lock); + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int irq = control->interrupt_position; + struct sdca_interrupt *interrupt; + const char *name; + int ret; + + if (irq == SDCA_NO_INTERRUPT) { + continue; + } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) { + dev_err(dev, "bad irq position: %d\n", irq); + return -EINVAL; + } + + interrupt = &info->irqs[irq]; + + if (interrupt->externally_requested) { + dev_dbg(dev, + "skipping irq %d, externally requested\n", + irq); + continue; + } + + ret = sdca_irq_data_populate(component, function, entity, + control, interrupt); + if (ret) + return ret; + + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, + base_handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + name, ret); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA_IRQ"); + +/** + * sdca_irq_allocate - allocate an SDCA interrupt structure for a device + * @dev: Device pointer against which things should be allocated. + * @regmap: regmap to be used for accessing the SDCA IRQ registers. + * @irq: The interrupt number. + * + * Typically this would be called from the top level driver for the whole + * SDCA device, as only a single instance is required across all Functions + * on the device. + * + * Return: A pointer to the allocated sdca_interrupt_info struct, or an + * error code. + */ +struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, + struct regmap *regmap, int irq) +{ + struct sdca_interrupt_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + info->irq_chip = sdca_irq_chip; + + devm_mutex_init(dev, &info->irq_lock); + + ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, + &info->irq_chip, &info->irq_data); + if (ret) { + dev_err(dev, "failed to register irq chip: %d\n", ret); + return ERR_PTR(ret); + } + + dev_dbg(dev, "registered on irq %d\n", irq); + + return info; +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA_IRQ"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SDCA IRQ library"); -- GitLab From b9ab3b61824190b1c6b2c59e7ba4de591f24eb92 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 24 Jun 2025 13:28:44 +0100 Subject: [PATCH 326/868] ASoC: SDCA: Add some initial IRQ handlers Add basic IRQ handlers for the function status and jack detection interrupts. Reviewed-by: Bard Liao Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250624122844.2761627-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 3 + sound/soc/sdca/sdca_interrupts.c | 157 ++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 4cda8b75bae0e..bbbc3ab27ebae 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -27,6 +27,7 @@ struct sdca_function_data; * @function: Pointer to the Function that the interrupt is associated with. * @entity: Pointer to the Entity that the interrupt is associated with. * @control: Pointer to the Control that the interrupt is associated with. + * @priv: Pointer to private data for use by the handler. * @externally_requested: Internal flag used to check if a client driver has * already requested the interrupt, for custom handling, allowing the core to * skip handling this interrupt. @@ -39,6 +40,8 @@ struct sdca_interrupt { struct sdca_entity *entity; struct sdca_control *control; + void *priv; + bool externally_requested; }; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 7272d11cb6d49..edb045c7ebb08 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -7,6 +7,7 @@ * https://www.mipi.org/mipi-sdca-v1-0-download */ +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \ SDW_SCP_SDCA_INTMASK_SDCA_##number) @@ -80,6 +82,143 @@ static irqreturn_t base_handler(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t function_status_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct device *dev = interrupt->component->dev; + unsigned int reg, val; + unsigned long status; + unsigned int mask; + int ret; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + interrupt->control->sel, 0); + + ret = regmap_read(interrupt->component->regmap, reg, &val); + if (ret < 0) { + dev_err(dev, "failed to read function status: %d\n", ret); + return IRQ_NONE; + } + + dev_dbg(dev, "function status: %#x\n", val); + + status = val; + for_each_set_bit(mask, &status, BITS_PER_BYTE) { + mask = 1 << mask; + + switch (mask) { + case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION: + //FIXME: Add init writes + break; + case SDCA_CTL_ENTITY_0_FUNCTION_FAULT: + dev_err(dev, "function fault\n"); + break; + case SDCA_CTL_ENTITY_0_UMP_SEQUENCE_FAULT: + dev_err(dev, "ump sequence fault\n"); + break; + case SDCA_CTL_ENTITY_0_FUNCTION_BUSY: + dev_info(dev, "unexpected function busy\n"); + break; + case SDCA_CTL_ENTITY_0_DEVICE_NEWLY_ATTACHED: + case SDCA_CTL_ENTITY_0_INTS_DISABLED_ABNORMALLY: + case SDCA_CTL_ENTITY_0_STREAMING_STOPPED_ABNORMALLY: + case SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET: + break; + } + } + + ret = regmap_write(interrupt->component->regmap, reg, val); + if (ret < 0) { + dev_err(dev, "failed to clear function status: %d\n", ret); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t detected_mode_handler(int irq, void *data) +{ + struct sdca_interrupt *interrupt = data; + struct snd_soc_component *component = interrupt->component; + struct device *dev = component->dev; + struct snd_soc_card *card = component->card; + struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; + struct snd_kcontrol *kctl = interrupt->priv; + struct snd_ctl_elem_value ucontrol; + struct soc_enum *soc_enum; + unsigned int reg, val; + int ret; + + if (!kctl) { + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", + interrupt->entity->label, + SDCA_CTL_SELECTED_MODE_NAME); + + if (!name) + return -ENOMEM; + + kctl = snd_soc_component_get_kcontrol(component, name); + if (!kctl) { + dev_dbg(dev, "control not found: %s\n", name); + return IRQ_NONE; + } + + interrupt->priv = kctl; + } + + soc_enum = (struct soc_enum *)kctl->private_value; + + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, + interrupt->control->sel, 0); + + ret = regmap_read(component->regmap, reg, &val); + if (ret < 0) { + dev_err(dev, "failed to read detected mode: %d\n", ret); + return IRQ_NONE; + } + + switch (val) { + case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS: + case SDCA_DETECTED_MODE_JACK_UNKNOWN: + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, + interrupt->entity->id, + SDCA_CTL_GE_SELECTED_MODE, 0); + + /* + * Selected mode is not normally marked as volatile register + * (RW), but here force a read from the hardware. If the + * detected mode is unknown we need to see what the device + * selected as a "safe" option. + */ + regcache_drop_region(component->regmap, reg, reg); + + ret = regmap_read(component->regmap, reg, &val); + if (ret) { + dev_err(dev, "failed to re-check selected mode: %d\n", ret); + return IRQ_NONE; + } + break; + default: + break; + } + + dev_dbg(dev, "%s: %#x\n", interrupt->name, val); + + ucontrol.value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + + down_write(rwsem); + ret = kctl->put(kctl, &ucontrol); + up_write(rwsem); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return IRQ_NONE; + } + + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + + return IRQ_HANDLED; +} + static int sdca_irq_request_locked(struct device *dev, struct sdca_interrupt_info *info, int sdca_irq, const char *name, @@ -202,6 +341,7 @@ int sdca_irq_populate(struct sdca_function_data *function, struct sdca_control *control = &entity->controls[j]; int irq = control->interrupt_position; struct sdca_interrupt *interrupt; + irq_handler_t handler; const char *name; int ret; @@ -226,8 +366,23 @@ int sdca_irq_populate(struct sdca_function_data *function, if (ret) return ret; + handler = base_handler; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_ENTITY_0: + if (control->sel == SDCA_CTL_ENTITY_0_FUNCTION_STATUS) + handler = function_status_handler; + break; + case SDCA_ENTITY_TYPE_GE: + if (control->sel == SDCA_CTL_GE_DETECTED_MODE) + handler = detected_mode_handler; + break; + default: + break; + } + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, - base_handler, interrupt); + handler, interrupt); if (ret) { dev_err(dev, "failed to request irq %s: %d\n", name, ret); -- GitLab From 1caf3f78c02262953c420f6d11bf6dd8202eae5f Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 4 Jun 2025 10:29:56 +0800 Subject: [PATCH 327/868] ACPI: pfr_update: Add more debug information when firmware update failed Users reported insufficient error information for debugging during firmware update failures on certain platforms. Add verbose error logs in the error code path to enhance debuggability. Reported-by: "Govindarajulu, Hariganesh" Signed-off-by: Chen Yu Link: https://patch.msgid.link/20250604022956.3723438-1-yu.c.chen@intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pfr_update.c | 63 +++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 031d1ba81b866..318683744ed17 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -127,8 +127,11 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, pfru_dev->rev_id, PFRU_FUNC_QUERY_UPDATE_CAP, NULL, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Query cap failed with no object\n"); return ret; + } if (out_obj->package.count < CAP_NR_IDX || out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER || @@ -141,13 +144,17 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER || out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER || - out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) + out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) { + dev_dbg(pfru_dev->parent_dev, + "Query cap failed with invalid package count/type\n"); goto free_acpi_buffer; + } cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; if (cap_hdr->status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status); + dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n", + cap_hdr->status); goto free_acpi_buffer; } @@ -193,24 +200,32 @@ static int query_buffer(struct pfru_com_buf_info *info, out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF, NULL, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with no object\n"); return ret; + } if (out_obj->package.count < BUF_NR_IDX || out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER || - out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) + out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) { + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with invalid package count/type\n"); goto free_acpi_buffer; + } info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value; info->ext_status = out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value; if (info->status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status); - dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status); + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with Error Status:%d\n", info->status); + dev_dbg(pfru_dev->parent_dev, + "Query buf failed with Error Extended Status:%d\n", info->ext_status); goto free_acpi_buffer; } @@ -295,12 +310,16 @@ static bool applicable_image(const void *data, struct pfru_update_cap_info *cap, m_img_hdr = data + size; type = get_image_type(m_img_hdr, pfru_dev); - if (type < 0) + if (type < 0) { + dev_dbg(pfru_dev->parent_dev, "Invalid image type\n"); return false; + } size = adjust_efi_size(m_img_hdr, size); - if (size < 0) + if (size < 0) { + dev_dbg(pfru_dev->parent_dev, "Invalid image size\n"); return false; + } auth = data + size; size += sizeof(u64) + auth->auth_info.hdr.len; @@ -346,8 +365,11 @@ static int start_update(int action, struct pfru_device *pfru_dev) out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, pfru_dev->rev_id, PFRU_FUNC_START, &in_obj, ACPI_TYPE_PACKAGE); - if (!out_obj) + if (!out_obj) { + dev_dbg(pfru_dev->parent_dev, + "Update failed to start with no object\n"); return ret; + } if (out_obj->package.count < UPDATE_NR_IDX || out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER || @@ -355,8 +377,11 @@ static int start_update(int action, struct pfru_device *pfru_dev) out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER || out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || - out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) { + dev_dbg(pfru_dev->parent_dev, + "Update failed with invalid package count/type\n"); goto free_acpi_buffer; + } update_result.status = out_obj->package.elements[UPDATE_STATUS_IDX].integer.value; @@ -365,8 +390,10 @@ static int start_update(int action, struct pfru_device *pfru_dev) if (update_result.status != DSM_SUCCEED) { ret = -EBUSY; - dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status); - dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", + dev_dbg(pfru_dev->parent_dev, + "Update failed with Error Status:%d\n", update_result.status); + dev_dbg(pfru_dev->parent_dev, + "Update failed with Error Extended Status:%d\n", update_result.ext_status); goto free_acpi_buffer; @@ -450,8 +477,10 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, if (ret) return ret; - if (len > buf_info.buf_size) + if (len > buf_info.buf_size) { + dev_dbg(pfru_dev->parent_dev, "Capsule image size too large\n"); return -EINVAL; + } iov.iov_base = (void __user *)buf; iov.iov_len = len; @@ -460,10 +489,14 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, /* map the communication buffer */ phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); - if (!buf_ptr) + if (!buf_ptr) { + dev_dbg(pfru_dev->parent_dev, "Failed to remap the buffer\n"); return -ENOMEM; + } if (!copy_from_iter_full(buf_ptr, len, &iter)) { + dev_dbg(pfru_dev->parent_dev, + "Failed to copy the data from the user space buffer\n"); ret = -EINVAL; goto unmap; } -- GitLab From 0c8fe93f4e7767b0e579959de051dcaddd7197fd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 30 Jun 2025 13:12:25 +0300 Subject: [PATCH 328/868] platform/chrome: chromeos_laptop: Remove duplicate check fwnode_remove_software_node() is aware of invalid input, no need to perform checks in the caller. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250630101225.1855431-1-andriy.shevchenko@linux.intel.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/chromeos_laptop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 3ab668764383f..3579c42b515ef 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -782,8 +782,7 @@ err_out: while (--i >= 0) { i2c_dev = &i2c_peripherals[i]; info = &i2c_dev->board_info; - if (!IS_ERR_OR_NULL(info->fwnode)) - fwnode_remove_software_node(info->fwnode); + fwnode_remove_software_node(info->fwnode); } kfree(i2c_peripherals); return error; -- GitLab From 5af89b6309417bdc2ff6835509d33a172c4a3218 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 30 Jun 2025 13:17:45 +0300 Subject: [PATCH 329/868] platform/chrome: chromeos_laptop: Replace open coded variant of DEFINE_RES_IRQ() DEFINE_RES_*() are compound literals, and hence no need to do that explicitly. Besides that, we have no IRQ name provided, no need to use _NAMED() variant. Replace open coded variant of DEFINE_RES_IRQ(). No functional changes intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250630101745.1855918-1-andriy.shevchenko@linux.intel.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/chromeos_laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 3579c42b515ef..0b92047265dea 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -726,9 +726,9 @@ static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev) if (irq < 0) return irq; - i2c_dev->irq_resource = (struct resource) - DEFINE_RES_NAMED(irq, 1, NULL, - IORESOURCE_IRQ | i2c_dev->irqflags); + i2c_dev->irq_resource = DEFINE_RES_IRQ(irq); + i2c_dev->irq_resource.flags |= i2c_dev->irqflags; + i2c_dev->board_info.resources = &i2c_dev->irq_resource; i2c_dev->board_info.num_resources = 1; } -- GitLab From 244bc18e5f1875401a4af87d2eae3f9376d9d720 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 30 Jun 2025 14:35:25 -0500 Subject: [PATCH 330/868] spi: stm32: delete stray tabs in stm32h7_spi_data_idleness() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These lines were indented one tab more than they should be. Delete the stray tabs. Signed-off-by: Dan Carpenter Acked-by: Alain Volmat Reviewed-by: Clément Le Goffic Link: https://patch.msgid.link/2033b9fa-7b0f-4617-b94e-7b0a51c5c4b1@sabinyo.mountain Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 9b3bc0c908bf2..a29c67024d00b 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1897,8 +1897,8 @@ static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer if (spi_delay_ns) { dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n", spi_delay_ns); - spi->cur_midi = spi_delay_ns; - } + spi->cur_midi = spi_delay_ns; + } } else { spi->cur_midi = spi_delay_ns; } -- GitLab From 09d55a54b466d60a71573c78a99a901410ef73e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:31:56 +0100 Subject: [PATCH 331/868] dt-bindings: mfd: adp5585: ease on the required properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not mandatory to use all the capabilities of the device. One can very well only use it as a gpio controller without the PWM support. This will be even more evident when support for the matrix keymap is added. Hence drop the requirements for PWM and GPIO. Acked-by: "Rob Herring (Arm)" Reviewed-by: Laurent Pinchart Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-1-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/adi,adp5585.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml index ee2272f754a33..e30e22f964f78 100644 --- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml +++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml @@ -52,9 +52,6 @@ patternProperties: required: - compatible - reg - - gpio-controller - - "#gpio-cells" - - "#pwm-cells" allOf: - if: -- GitLab From 175f199085c1253d2683f583ce32b2e02cd70de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:31:57 +0100 Subject: [PATCH 332/868] mfd: adp5585: Only add devices given in FW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all devices (features) of the adp5585 device are mandatory to be used in all platforms. Hence, check what's given in FW and dynamically create the mfd_cell array to be given to devm_mfd_add_devices(). Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-2-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 160e0b38106a6..53a46734f2d02 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -17,7 +17,13 @@ #include #include -static const struct mfd_cell adp5585_devs[] = { +enum { + ADP5585_DEV_GPIO, + ADP5585_DEV_PWM, + ADP5585_DEV_MAX +}; + +static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = { { .name = "adp5585-gpio", }, { .name = "adp5585-pwm", }, }; @@ -110,6 +116,27 @@ static const struct regmap_config adp5585_regmap_configs[] = { }, }; +static int adp5585_add_devices(struct device *dev) +{ + int ret; + + if (device_property_present(dev, "#pwm-cells")) { + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to add PWM device\n"); + } + + if (device_property_present(dev, "#gpio-cells")) { + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, + &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to add GPIO device\n"); + } + + return 0; +} + static int adp5585_i2c_probe(struct i2c_client *i2c) { const struct regmap_config *regmap_config; @@ -138,14 +165,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, -ENODEV, "Invalid device ID 0x%02x\n", id); - ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, - adp5585_devs, ARRAY_SIZE(adp5585_devs), - NULL, 0, NULL); - if (ret) - return dev_err_probe(&i2c->dev, ret, - "Failed to add child devices\n"); - - return 0; + return adp5585_add_devices(&i2c->dev); } static int adp5585_suspend(struct device *dev) -- GitLab From e551760164a74ac00916fad64ac2d0b1d3d714c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:31:58 +0100 Subject: [PATCH 333/868] mfd: adp5585: Enable oscillator during probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure to enable the oscillator in the top device. This will allow to not control this in the child PWM device as that would not work with future support for keyboard matrix where the oscillator needs to be always enabled (and so cannot be disabled by disabling PWM). Reviewed-by: Laurent Pinchart Signed-off-by: Nuno Sá Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-3-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 19 +++++++++++++++++++ drivers/pwm/pwm-adp5585.c | 5 ----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 53a46734f2d02..e4a75ae9b2696 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -137,6 +137,13 @@ static int adp5585_add_devices(struct device *dev) return 0; } +static void adp5585_osc_disable(void *data) +{ + const struct adp5585_dev *adp5585 = data; + + regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0); +} + static int adp5585_i2c_probe(struct i2c_client *i2c) { const struct regmap_config *regmap_config; @@ -165,6 +172,18 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, -ENODEV, "Invalid device ID 0x%02x\n", id); + /* + * Enable the internal oscillator, as it's shared between multiple + * functions. + */ + ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585); + if (ret) + return ret; + return adp5585_add_devices(&i2c->dev); } diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c index d79106d121810..add36bc7d2216 100644 --- a/drivers/pwm/pwm-adp5585.c +++ b/drivers/pwm/pwm-adp5585.c @@ -63,7 +63,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip, int ret; if (!state->enabled) { - regmap_clear_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN); regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN); return 0; } @@ -101,10 +100,6 @@ static int pwm_adp5585_apply(struct pwm_chip *chip, if (ret) return ret; - ret = regmap_set_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN); - if (ret) - return ret; - return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN); } -- GitLab From e6545bdb1b7681da7edb6a34bed4be5e7f41cf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:31:59 +0100 Subject: [PATCH 334/868] mfd: adp5585: Make use of MFD_CELL_NAME() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the helper macro. No functional change intended... Whilst we're at it, now seems like a good time to update the Copyright. Reviewed-by: Laurent Pinchart Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-4-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index e4a75ae9b2696..c764f48187583 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -4,6 +4,7 @@ * * Copyright 2022 NXP * Copyright 2024 Ideas on Board Oy + * Copyright 2025 Analog Devices Inc. */ #include @@ -24,8 +25,8 @@ enum { }; static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = { - { .name = "adp5585-gpio", }, - { .name = "adp5585-pwm", }, + MFD_CELL_NAME("adp5585-gpio"), + MFD_CELL_NAME("adp5585-pwm"), }; static const struct regmap_range adp5585_volatile_ranges[] = { -- GitLab From e65e2b0d0f7e75c40f426e0f3e0a1bb6faff93e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:00 +0100 Subject: [PATCH 335/868] dt-bindings: mfd: adp5585: document adp5589 I/O expander MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder, programmable logic, reset generator, and PWM generator. We can't really have adp5589 devices fallback to adp5585 (which have less pins) because there are some significant differences in the register map. Reviewed-by: "Rob Herring (Arm)" Reviewed-by: Laurent Pinchart Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-5-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/adi,adp5585.yaml | 47 +++++++++++++++---- .../devicetree/bindings/trivial-devices.yaml | 2 - 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml index e30e22f964f78..9471af28419d8 100644 --- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml +++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml @@ -15,14 +15,21 @@ description: properties: compatible: - items: - - enum: - - adi,adp5585-00 # Default - - adi,adp5585-01 # 11 GPIOs - - adi,adp5585-02 # No pull-up resistors by default on special pins - - adi,adp5585-03 # Alternate I2C address - - adi,adp5585-04 # Pull-down resistors on all pins by default - - const: adi,adp5585 + oneOf: + - items: + - enum: + - adi,adp5585-00 # Default + - adi,adp5585-01 # 11 GPIOs + - adi,adp5585-02 # No pull-up resistors by default on special pins + - adi,adp5585-03 # Alternate I2C address + - adi,adp5585-04 # Pull-down resistors on all pins by default + - const: adi,adp5585 + - items: + - enum: + - adi,adp5589-00 # Default + - adi,adp5589-01 # R4 defaulted to RESET1 output + - adi,adp5589-02 # Pull-down resistors by default on special pins + - const: adi,adp5589 reg: maxItems: 1 @@ -62,7 +69,17 @@ allOf: then: properties: gpio-reserved-ranges: false - else: + + - if: + properties: + compatible: + contains: + enum: + - adi,adp5585-00 + - adi,adp5585-02 + - adi,adp5585-03 + - adi,adp5585-04 + then: properties: gpio-reserved-ranges: maxItems: 1 @@ -71,6 +88,18 @@ allOf: - const: 5 - const: 1 + - if: + properties: + compatible: + contains: + enum: + - adi,adp5589-00 + - adi,adp5589-01 + - adi,adp5589-02 + then: + properties: + gpio-reserved-ranges: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 27930708ccd58..43b21fed7ec01 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -39,8 +39,6 @@ properties: - ad,adm9240 # AD5110 - Nonvolatile Digital Potentiometer - adi,ad5110 - # Analog Devices ADP5589 Keypad Decoder and I/O Expansion - - adi,adp5589 # Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher - adi,lt7182s # AMS iAQ-Core VOC Sensor -- GitLab From 1a4eabf662543c62ae1e71a26d1c8e6643c66388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:01 +0100 Subject: [PATCH 336/868] mfd: adp5585: Refactor how regmap defaults are handled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only thing changing between variants is the regmap default registers. Hence, instead of having a regmap configuration for every variant (duplicating lots of fields), add a chip info type of structure with a regmap ID to identify which defaults to use and populate regmap_config at runtime given a template plus the id. Also note that between variants, the defaults can be the same which means the chip info structure can be used in more than one compatible. This will also make it simpler adding new chips with more variants. Also note that the chip info structures are deliberately not const as they will also contain lots of members that are the same between the different devices variants and so we will fill those at runtime. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-6-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 80 +++++++++++++++++++------------------ include/linux/mfd/adp5585.h | 11 +++++ 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index c764f48187583..4d92b63629289 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -81,42 +81,36 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = { /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, }; -enum adp5585_regmap_type { - ADP5585_REGMAP_00, - ADP5585_REGMAP_02, - ADP5585_REGMAP_04, +static const u8 *adp5585_regmap_defaults[ADP5585_MAX] = { + [ADP5585_00] = adp5585_regmap_defaults_00, + [ADP5585_01] = adp5585_regmap_defaults_00, + [ADP5585_02] = adp5585_regmap_defaults_02, + [ADP5585_03] = adp5585_regmap_defaults_00, + [ADP5585_04] = adp5585_regmap_defaults_04, }; -static const struct regmap_config adp5585_regmap_configs[] = { - [ADP5585_REGMAP_00] = { - .reg_bits = 8, - .val_bits = 8, - .max_register = ADP5585_MAX_REG, - .volatile_table = &adp5585_volatile_regs, - .cache_type = REGCACHE_MAPLE, - .reg_defaults_raw = adp5585_regmap_defaults_00, - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_00), - }, - [ADP5585_REGMAP_02] = { - .reg_bits = 8, - .val_bits = 8, - .max_register = ADP5585_MAX_REG, - .volatile_table = &adp5585_volatile_regs, - .cache_type = REGCACHE_MAPLE, - .reg_defaults_raw = adp5585_regmap_defaults_02, - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_02), - }, - [ADP5585_REGMAP_04] = { - .reg_bits = 8, - .val_bits = 8, - .max_register = ADP5585_MAX_REG, - .volatile_table = &adp5585_volatile_regs, - .cache_type = REGCACHE_MAPLE, - .reg_defaults_raw = adp5585_regmap_defaults_04, - .num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04), - }, +static const struct regmap_config adp5585_regmap_config_template = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ADP5585_MAX_REG, + .volatile_table = &adp5585_volatile_regs, + .cache_type = REGCACHE_MAPLE, + .num_reg_defaults_raw = ADP5585_MAX_REG + 1, }; +static struct regmap_config *adp5585_fill_regmap_config(const struct adp5585_dev *adp5585) +{ + struct regmap_config *regmap_config; + + regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, + sizeof(*regmap_config), GFP_KERNEL); + if (!regmap_config) + return ERR_PTR(-ENOMEM); + + regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant]; + return regmap_config; +} + static int adp5585_add_devices(struct device *dev) { int ret; @@ -147,7 +141,7 @@ static void adp5585_osc_disable(void *data) static int adp5585_i2c_probe(struct i2c_client *i2c) { - const struct regmap_config *regmap_config; + struct regmap_config *regmap_config; struct adp5585_dev *adp5585; unsigned int id; int ret; @@ -157,8 +151,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return -ENOMEM; i2c_set_clientdata(i2c, adp5585); + adp5585->dev = &i2c->dev; + + adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c); + if (!adp5585->variant) + return -ENODEV; + + regmap_config = adp5585_fill_regmap_config(adp5585); + if (IS_ERR(regmap_config)) + return PTR_ERR(regmap_config); - regmap_config = i2c_get_match_data(i2c); adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config); if (IS_ERR(adp5585->regmap)) return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap), @@ -212,19 +214,19 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume); static const struct of_device_id adp5585_of_match[] = { { .compatible = "adi,adp5585-00", - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00], + .data = (void *)ADP5585_00, }, { .compatible = "adi,adp5585-01", - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00], + .data = (void *)ADP5585_01, }, { .compatible = "adi,adp5585-02", - .data = &adp5585_regmap_configs[ADP5585_REGMAP_02], + .data = (void *)ADP5585_02, }, { .compatible = "adi,adp5585-03", - .data = &adp5585_regmap_configs[ADP5585_REGMAP_00], + .data = (void *)ADP5585_03, }, { .compatible = "adi,adp5585-04", - .data = &adp5585_regmap_configs[ADP5585_REGMAP_04], + .data = (void *)ADP5585_04, }, { /* sentinel */ } }; diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 016033cd68e46..c56af8d8d76c4 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -119,8 +119,19 @@ struct regmap; +enum adp5585_variant { + ADP5585_00 = 1, + ADP5585_01, + ADP5585_02, + ADP5585_03, + ADP5585_04, + ADP5585_MAX +}; + struct adp5585_dev { + struct device *dev; struct regmap *regmap; + enum adp5585_variant variant; }; #endif -- GitLab From 0190a72f28ee0995c546fd4fcf80ed25a0fc4b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:02 +0100 Subject: [PATCH 337/868] mfd: adp5585: Add support for adp5589 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADP5589 is a 19 I/O port expander with built-in keypad matrix decoder, programmable logic, reset generator, and PWM generator. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-7-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 123 +++++++++++++++++++++++++++++++++--- include/linux/mfd/adp5585.h | 10 +++ 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 4d92b63629289..00996571ef900 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -29,6 +29,11 @@ static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = { MFD_CELL_NAME("adp5585-pwm"), }; +static const struct mfd_cell adp5589_devs[] = { + MFD_CELL_NAME("adp5589-gpio"), + MFD_CELL_NAME("adp5589-pwm"), +}; + static const struct regmap_range adp5585_volatile_ranges[] = { regmap_reg_range(ADP5585_ID, ADP5585_GPI_STATUS_B), }; @@ -38,6 +43,15 @@ static const struct regmap_access_table adp5585_volatile_regs = { .n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges), }; +static const struct regmap_range adp5589_volatile_ranges[] = { + regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C), +}; + +static const struct regmap_access_table adp5589_volatile_regs = { + .yes_ranges = adp5589_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges), +}; + /* * Chip variants differ in the default configuration of pull-up and pull-down * resistors, and therefore have different default register values: @@ -81,12 +95,54 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = { /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = { + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = { + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +}; + +static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = { + /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00, + /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + static const u8 *adp5585_regmap_defaults[ADP5585_MAX] = { [ADP5585_00] = adp5585_regmap_defaults_00, [ADP5585_01] = adp5585_regmap_defaults_00, [ADP5585_02] = adp5585_regmap_defaults_02, [ADP5585_03] = adp5585_regmap_defaults_00, [ADP5585_04] = adp5585_regmap_defaults_04, + [ADP5589_00] = adp5589_regmap_defaults_00, + [ADP5589_01] = adp5589_regmap_defaults_01, + [ADP5589_02] = adp5589_regmap_defaults_02, }; static const struct regmap_config adp5585_regmap_config_template = { @@ -98,33 +154,69 @@ static const struct regmap_config adp5585_regmap_config_template = { .num_reg_defaults_raw = ADP5585_MAX_REG + 1, }; -static struct regmap_config *adp5585_fill_regmap_config(const struct adp5585_dev *adp5585) +static const struct regmap_config adp5589_regmap_config_template = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ADP5589_MAX_REG, + .volatile_table = &adp5589_volatile_regs, + .cache_type = REGCACHE_MAPLE, + .num_reg_defaults_raw = ADP5589_MAX_REG + 1, +}; + +static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585) { struct regmap_config *regmap_config; - regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, - sizeof(*regmap_config), GFP_KERNEL); + switch (adp5585->variant) { + case ADP5585_00: + case ADP5585_01: + case ADP5585_02: + case ADP5585_03: + case ADP5585_04: + adp5585->id = ADP5585_MAN_ID_VALUE; + regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, + sizeof(*regmap_config), GFP_KERNEL); + break; + case ADP5589_00: + case ADP5589_01: + case ADP5589_02: + adp5585->id = ADP5589_MAN_ID_VALUE; + regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template, + sizeof(*regmap_config), GFP_KERNEL); + break; + default: + return ERR_PTR(-ENODEV); + } + if (!regmap_config) return ERR_PTR(-ENOMEM); regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant]; + return regmap_config; } -static int adp5585_add_devices(struct device *dev) +static int adp5585_add_devices(const struct adp5585_dev *adp5585) { + struct device *dev = adp5585->dev; + const struct mfd_cell *cells; int ret; + if (adp5585->id == ADP5585_MAN_ID_VALUE) + cells = adp5585_devs; + else + cells = adp5589_devs; + if (device_property_present(dev, "#pwm-cells")) { ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, - &adp5585_devs[ADP5585_DEV_PWM], 1, NULL, 0, NULL); + &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL); if (ret) return dev_err_probe(dev, ret, "Failed to add PWM device\n"); } if (device_property_present(dev, "#gpio-cells")) { ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, - &adp5585_devs[ADP5585_DEV_GPIO], 1, NULL, 0, NULL); + &cells[ADP5585_DEV_GPIO], 1, NULL, 0, NULL); if (ret) return dev_err_probe(dev, ret, "Failed to add GPIO device\n"); } @@ -157,7 +249,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) if (!adp5585->variant) return -ENODEV; - regmap_config = adp5585_fill_regmap_config(adp5585); + regmap_config = adp5585_fill_variant_config(adp5585); if (IS_ERR(regmap_config)) return PTR_ERR(regmap_config); @@ -171,7 +263,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, ret, "Failed to read device ID\n"); - if ((id & ADP5585_MAN_ID_MASK) != ADP5585_MAN_ID_VALUE) + id &= ADP5585_MAN_ID_MASK; + if (id != adp5585->id) return dev_err_probe(&i2c->dev, -ENODEV, "Invalid device ID 0x%02x\n", id); @@ -187,7 +280,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) if (ret) return ret; - return adp5585_add_devices(&i2c->dev); + return adp5585_add_devices(adp5585); } static int adp5585_suspend(struct device *dev) @@ -227,6 +320,18 @@ static const struct of_device_id adp5585_of_match[] = { }, { .compatible = "adi,adp5585-04", .data = (void *)ADP5585_04, + }, { + .compatible = "adi,adp5589-00", + .data = (void *)ADP5589_00, + }, { + .compatible = "adi,adp5589-01", + .data = (void *)ADP5589_01, + }, { + .compatible = "adi,adp5589-02", + .data = (void *)ADP5589_02, + }, { + .compatible = "adi,adp5589", + .data = (void *)ADP5589_00, }, { /* sentinel */ } }; diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index c56af8d8d76c4..70e58122a36a7 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -117,6 +117,12 @@ #define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0) #define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n)) +/* ADP5589 */ +#define ADP5589_MAN_ID_VALUE 0x10 +#define ADP5589_GPI_STATUS_C 0x18 +#define ADP5589_INT_EN 0x4e +#define ADP5589_MAX_REG ADP5589_INT_EN + struct regmap; enum adp5585_variant { @@ -125,6 +131,9 @@ enum adp5585_variant { ADP5585_02, ADP5585_03, ADP5585_04, + ADP5589_00, + ADP5589_01, + ADP5589_02, ADP5585_MAX }; @@ -132,6 +141,7 @@ struct adp5585_dev { struct device *dev; struct regmap *regmap; enum adp5585_variant variant; + unsigned int id; }; #endif -- GitLab From 7077fb501b95360c7fe35553f2bdb1ccf34edd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:03 +0100 Subject: [PATCH 338/868] mfd: adp5585: Add a per chip reg struture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some differences in the register map between the devices. Hence, add a register structure per device. This will be needed in following patches. On top of that adp5585_fill_regmap_config() is renamed and reworked so that the current struct adp5585_info act as template (they indeed contain all the different data between variants) which can then be complemented depending on the device (as identified by the id register). This is done like this since a lot of the data is pretty much the same between variants of the same device. Reviewed-by: Lee Jones Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-8-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 10 ++++++++++ include/linux/mfd/adp5585.h | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 00996571ef900..ae12372bdde91 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -163,6 +163,14 @@ static const struct regmap_config adp5589_regmap_config_template = { .num_reg_defaults_raw = ADP5589_MAX_REG + 1, }; +static const struct adp5585_regs adp5585_regs = { + .ext_cfg = ADP5585_PIN_CONFIG_C, +}; + +static const struct adp5585_regs adp5589_regs = { + .ext_cfg = ADP5589_PIN_CONFIG_D, +}; + static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585) { struct regmap_config *regmap_config; @@ -174,6 +182,7 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp case ADP5585_03: case ADP5585_04: adp5585->id = ADP5585_MAN_ID_VALUE; + adp5585->regs = &adp5585_regs; regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, sizeof(*regmap_config), GFP_KERNEL); break; @@ -181,6 +190,7 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp case ADP5589_01: case ADP5589_02: adp5585->id = ADP5589_MAN_ID_VALUE; + adp5585->regs = &adp5589_regs; regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template, sizeof(*regmap_config), GFP_KERNEL); break; diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 70e58122a36a7..6ecb90a6276c0 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -120,6 +120,7 @@ /* ADP5589 */ #define ADP5589_MAN_ID_VALUE 0x10 #define ADP5589_GPI_STATUS_C 0x18 +#define ADP5589_PIN_CONFIG_D 0x4C #define ADP5589_INT_EN 0x4e #define ADP5589_MAX_REG ADP5589_INT_EN @@ -137,9 +138,14 @@ enum adp5585_variant { ADP5585_MAX }; +struct adp5585_regs { + unsigned int ext_cfg; +}; + struct adp5585_dev { struct device *dev; struct regmap *regmap; + const struct adp5585_regs *regs; enum adp5585_variant variant; unsigned int id; }; -- GitLab From 9f425bf713b511b1078e0fea5a88c497e13dbb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:04 +0100 Subject: [PATCH 339/868] gpio: adp5585: add support for the adp5589 expander MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the adp5589 I/O expander which supports up to 19 pins. We need to add a chip_info based struct since accessing register "banks" and "bits" differs between devices. Also some register addresses are different. While at it move ADP558X_GPIO_MAX defines to the main header file and rename them. That information will be needed by the top level device in a following change. Acked-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-9-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/gpio/gpio-adp5585.c | 151 ++++++++++++++++++++++++++++-------- include/linux/mfd/adp5585.h | 18 ++--- 2 files changed, 126 insertions(+), 43 deletions(-) diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index d5c0f1b267c82..cdf107742579c 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -4,6 +4,7 @@ * * Copyright 2022 NXP * Copyright 2024 Ideas on Board Oy + * Copyright 2025 Analog Devices, Inc. */ #include @@ -14,57 +15,106 @@ #include #include -#define ADP5585_GPIO_MAX 11 +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the + * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to + * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver + * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is + * marked as reserved in the device tree for variants that don't support it. + */ +#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0) +#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n)) + +/* + * Bank 0 covers pins "GPIO 1/R0" to "GPIO 8/R7", numbered 0 to 7 by the + * driver, bank 1 covers pins "GPIO 9/C0" to "GPIO 16/C7", numbered 8 to + * 15 and bank 3 covers pins "GPIO 17/C8" to "GPIO 19/C10", numbered 16 to 18. + */ +#define ADP5589_BANK(n) ((n) >> 3) +#define ADP5589_BIT(n) BIT((n) & 0x7) + +struct adp5585_gpio_chip { + int (*bank)(unsigned int off); + int (*bit)(unsigned int off); + unsigned int max_gpio; + unsigned int debounce_dis_a; + unsigned int rpull_cfg_a; + unsigned int gpo_data_a; + unsigned int gpo_out_a; + unsigned int gpio_dir_a; + unsigned int gpi_stat_a; + bool has_bias_hole; +}; struct adp5585_gpio_dev { struct gpio_chip gpio_chip; + const struct adp5585_gpio_chip *info; struct regmap *regmap; }; +static int adp5585_gpio_bank(unsigned int off) +{ + return ADP5585_BANK(off); +} + +static int adp5585_gpio_bit(unsigned int off) +{ + return ADP5585_BIT(off); +} + +static int adp5589_gpio_bank(unsigned int off) +{ + return ADP5589_BANK(off); +} + +static int adp5589_gpio_bit(unsigned int off) +{ + return ADP5589_BIT(off); +} + static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; unsigned int val; - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val); + regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), &val); - return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + return val & info->bit(off) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; - return regmap_clear_bits(adp5585_gpio->regmap, - ADP5585_GPIO_DIRECTION_A + bank, bit); + return regmap_clear_bits(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), + info->bit(off)); } static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bank = info->bank(off); + unsigned int bit = info->bit(off); int ret; - ret = regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_DATA_OUT_A + bank, bit, - val ? bit : 0); + ret = regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + bank, + bit, val ? bit : 0); if (ret) return ret; - return regmap_set_bits(adp5585_gpio->regmap, - ADP5585_GPIO_DIRECTION_A + bank, bit); + return regmap_set_bits(adp5585_gpio->regmap, info->gpio_dir_a + bank, + bit); } static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bank = info->bank(off); + unsigned int bit = info->bit(off); unsigned int reg; unsigned int val; @@ -79,8 +129,8 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off) * .direction_input(), .direction_output() or .set() operations racing * with this. */ - regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val); - reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A; + regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + bank, &val); + reg = val & bit ? info->gpo_data_a : info->gpi_stat_a; regmap_read(adp5585_gpio->regmap, reg + bank, &val); return !!(val & bit); @@ -90,17 +140,17 @@ static int adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off, int val) { struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); - return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_DATA_OUT_A + bank, + return regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + info->bank(off), bit, val ? bit : 0); } static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, unsigned int bias) { + const struct adp5585_gpio_chip *info = adp5585_gpio->info; unsigned int bit, reg, mask, val; /* @@ -108,8 +158,10 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, * consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits * after R5. */ - bit = off * 2 + (off > 5 ? 4 : 0); - reg = ADP5585_RPULL_CONFIG_A + bit / 8; + bit = off * 2; + if (info->has_bias_hole) + bit += (off > 5 ? 4 : 0); + reg = info->rpull_cfg_a + bit / 8; mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8); val = bias << (bit % 8); @@ -119,22 +171,22 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio, static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, enum pin_config_param drive) { - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_GPO_OUT_MODE_A + bank, bit, + info->gpo_out_a + info->bank(off), bit, drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0); } static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio, unsigned int off, unsigned int debounce) { - unsigned int bank = ADP5585_BANK(off); - unsigned int bit = ADP5585_BIT(off); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + unsigned int bit = adp5585_gpio->info->bit(off); return regmap_update_bits(adp5585_gpio->regmap, - ADP5585_DEBOUNCE_DIS_A + bank, bit, + info->debounce_dis_a + info->bank(off), bit, debounce ? 0 : bit); } @@ -175,6 +227,7 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off, static int adp5585_gpio_probe(struct platform_device *pdev) { struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); + const struct platform_device_id *id = platform_get_device_id(pdev); struct adp5585_gpio_dev *adp5585_gpio; struct device *dev = &pdev->dev; struct gpio_chip *gc; @@ -186,6 +239,10 @@ static int adp5585_gpio_probe(struct platform_device *pdev) adp5585_gpio->regmap = adp5585->regmap; + adp5585_gpio->info = (const struct adp5585_gpio_chip *)id->driver_data; + if (!adp5585_gpio->info) + return -ENODEV; + device_set_of_node_from_dev(dev, dev->parent); gc = &adp5585_gpio->gpio_chip; @@ -199,7 +256,7 @@ static int adp5585_gpio_probe(struct platform_device *pdev) gc->can_sleep = true; gc->base = -1; - gc->ngpio = ADP5585_GPIO_MAX; + gc->ngpio = adp5585_gpio->info->max_gpio; gc->label = pdev->name; gc->owner = THIS_MODULE; @@ -211,8 +268,34 @@ static int adp5585_gpio_probe(struct platform_device *pdev) return 0; } +static const struct adp5585_gpio_chip adp5585_gpio_chip_info = { + .bank = adp5585_gpio_bank, + .bit = adp5585_gpio_bit, + .debounce_dis_a = ADP5585_DEBOUNCE_DIS_A, + .rpull_cfg_a = ADP5585_RPULL_CONFIG_A, + .gpo_data_a = ADP5585_GPO_DATA_OUT_A, + .gpo_out_a = ADP5585_GPO_OUT_MODE_A, + .gpio_dir_a = ADP5585_GPIO_DIRECTION_A, + .gpi_stat_a = ADP5585_GPI_STATUS_A, + .max_gpio = ADP5585_PIN_MAX, + .has_bias_hole = true, +}; + +static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { + .bank = adp5589_gpio_bank, + .bit = adp5589_gpio_bit, + .debounce_dis_a = ADP5589_DEBOUNCE_DIS_A, + .rpull_cfg_a = ADP5589_RPULL_CONFIG_A, + .gpo_data_a = ADP5589_GPO_DATA_OUT_A, + .gpo_out_a = ADP5589_GPO_OUT_MODE_A, + .gpio_dir_a = ADP5589_GPIO_DIRECTION_A, + .gpi_stat_a = ADP5589_GPI_STATUS_A, + .max_gpio = ADP5589_PIN_MAX, +}; + static const struct platform_device_id adp5585_gpio_id_table[] = { - { "adp5585-gpio" }, + { "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info }, + { "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table); diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 6ecb90a6276c0..d26f722cf31af 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -107,23 +107,23 @@ #define ADP5585_MAX_REG ADP5585_INT_EN -/* - * Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the - * driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to - * 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver - * uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is - * marked as reserved in the device tree for variants that don't support it. - */ -#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0) -#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n)) +#define ADP5585_PIN_MAX 11 /* ADP5589 */ #define ADP5589_MAN_ID_VALUE 0x10 +#define ADP5589_GPI_STATUS_A 0x16 #define ADP5589_GPI_STATUS_C 0x18 +#define ADP5589_RPULL_CONFIG_A 0x19 +#define ADP5589_DEBOUNCE_DIS_A 0x27 +#define ADP5589_GPO_DATA_OUT_A 0x2a +#define ADP5589_GPO_OUT_MODE_A 0x2d +#define ADP5589_GPIO_DIRECTION_A 0x30 #define ADP5589_PIN_CONFIG_D 0x4C #define ADP5589_INT_EN 0x4e #define ADP5589_MAX_REG ADP5589_INT_EN +#define ADP5589_PIN_MAX 19 + struct regmap; enum adp5585_variant { -- GitLab From 75024f97e82e63d02b0743500efb1e264a1c2dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:05 +0100 Subject: [PATCH 340/868] pwm: adp5585: add support for adp5589 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the adp5589 I/O expander. From a PWM point of view it is pretty similar to adp5585. Main difference is the address of registers meaningful for configuring the PWM. Acked-by: Uwe Kleine-König Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-10-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/pwm/pwm-adp5585.c | 73 ++++++++++++++++++++++++++++--------- include/linux/mfd/adp5585.h | 3 ++ 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c index add36bc7d2216..dc2860979e24e 100644 --- a/drivers/pwm/pwm-adp5585.c +++ b/drivers/pwm/pwm-adp5585.c @@ -33,21 +33,33 @@ #define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) #define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) +struct adp5585_pwm_chip { + unsigned int pwm_cfg; + unsigned int pwm_offt_low; + unsigned int pwm_ont_low; +}; + +struct adp5585_pwm { + const struct adp5585_pwm_chip *info; + struct regmap *regmap; + unsigned int ext_cfg; +}; + static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct regmap *regmap = pwmchip_get_drvdata(chip); + struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip); /* Configure the R3 pin as PWM output. */ - return regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C, + return regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg, ADP5585_R3_EXTEND_CFG_MASK, ADP5585_R3_EXTEND_CFG_PWM_OUT); } static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct regmap *regmap = pwmchip_get_drvdata(chip); + struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip); - regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C, + regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg, ADP5585_R3_EXTEND_CFG_MASK, ADP5585_R3_EXTEND_CFG_GPIO4); } @@ -56,14 +68,16 @@ static int pwm_adp5585_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct regmap *regmap = pwmchip_get_drvdata(chip); + struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip); + const struct adp5585_pwm_chip *info = adp5585_pwm->info; + struct regmap *regmap = adp5585_pwm->regmap; u64 period, duty_cycle; u32 on, off; __le16 val; int ret; if (!state->enabled) { - regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN); + regmap_clear_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN); return 0; } @@ -84,41 +98,43 @@ static int pwm_adp5585_apply(struct pwm_chip *chip, off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on; val = cpu_to_le16(off); - ret = regmap_bulk_write(regmap, ADP5585_PWM_OFFT_LOW, &val, 2); + ret = regmap_bulk_write(regmap, info->pwm_offt_low, &val, 2); if (ret) return ret; val = cpu_to_le16(on); - ret = regmap_bulk_write(regmap, ADP5585_PWM_ONT_LOW, &val, 2); + ret = regmap_bulk_write(regmap, info->pwm_ont_low, &val, 2); if (ret) return ret; /* Enable PWM in continuous mode and no external AND'ing. */ - ret = regmap_update_bits(regmap, ADP5585_PWM_CFG, + ret = regmap_update_bits(regmap, info->pwm_cfg, ADP5585_PWM_IN_AND | ADP5585_PWM_MODE | ADP5585_PWM_EN, ADP5585_PWM_EN); if (ret) return ret; - return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN); + return regmap_set_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN); } static int pwm_adp5585_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { - struct regmap *regmap = pwmchip_get_drvdata(chip); + struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip); + const struct adp5585_pwm_chip *info = adp5585_pwm->info; + struct regmap *regmap = adp5585_pwm->regmap; unsigned int on, off; unsigned int val; __le16 on_off; int ret; - ret = regmap_bulk_read(regmap, ADP5585_PWM_OFFT_LOW, &on_off, 2); + ret = regmap_bulk_read(regmap, info->pwm_offt_low, &on_off, 2); if (ret) return ret; off = le16_to_cpu(on_off); - ret = regmap_bulk_read(regmap, ADP5585_PWM_ONT_LOW, &on_off, 2); + ret = regmap_bulk_read(regmap, info->pwm_ont_low, &on_off, 2); if (ret) return ret; on = le16_to_cpu(on_off); @@ -128,7 +144,7 @@ static int pwm_adp5585_get_state(struct pwm_chip *chip, state->polarity = PWM_POLARITY_NORMAL; - regmap_read(regmap, ADP5585_PWM_CFG, &val); + regmap_read(regmap, info->pwm_cfg, &val); state->enabled = !!(val & ADP5585_PWM_EN); return 0; @@ -143,18 +159,28 @@ static const struct pwm_ops adp5585_pwm_ops = { static int adp5585_pwm_probe(struct platform_device *pdev) { + const struct platform_device_id *id = platform_get_device_id(pdev); struct device *dev = &pdev->dev; struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + struct adp5585_pwm *adp5585_pwm; struct pwm_chip *chip; int ret; - chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM, 0); + chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM, + sizeof(*adp5585_pwm)); if (IS_ERR(chip)) return PTR_ERR(chip); + adp5585_pwm = pwmchip_get_drvdata(chip); + adp5585_pwm->regmap = adp5585->regmap; + adp5585_pwm->ext_cfg = adp5585->regs->ext_cfg; + + adp5585_pwm->info = (const struct adp5585_pwm_chip *)id->driver_data; + if (!adp5585_pwm->info) + return -ENODEV; + device_set_of_node_from_dev(dev, dev->parent); - pwmchip_set_drvdata(chip, adp5585->regmap); chip->ops = &adp5585_pwm_ops; ret = devm_pwmchip_add(dev, chip); @@ -164,8 +190,21 @@ static int adp5585_pwm_probe(struct platform_device *pdev) return 0; } +static const struct adp5585_pwm_chip adp5589_pwm_chip_info = { + .pwm_cfg = ADP5585_PWM_CFG, + .pwm_offt_low = ADP5585_PWM_OFFT_LOW, + .pwm_ont_low = ADP5585_PWM_ONT_LOW, +}; + +static const struct adp5585_pwm_chip adp5585_pwm_chip_info = { + .pwm_cfg = ADP5589_PWM_CFG, + .pwm_offt_low = ADP5589_PWM_OFFT_LOW, + .pwm_ont_low = ADP5589_PWM_ONT_LOW, +}; + static const struct platform_device_id adp5585_pwm_id_table[] = { - { "adp5585-pwm" }, + { "adp5585-pwm", (kernel_ulong_t)&adp5585_pwm_chip_info }, + { "adp5589-pwm", (kernel_ulong_t)&adp5589_pwm_chip_info }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table); diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index d26f722cf31af..77f7c74f084dd 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -118,6 +118,9 @@ #define ADP5589_GPO_DATA_OUT_A 0x2a #define ADP5589_GPO_OUT_MODE_A 0x2d #define ADP5589_GPIO_DIRECTION_A 0x30 +#define ADP5589_PWM_OFFT_LOW 0x3e +#define ADP5589_PWM_ONT_LOW 0x40 +#define ADP5589_PWM_CFG 0x42 #define ADP5589_PIN_CONFIG_D 0x4C #define ADP5589_INT_EN 0x4e #define ADP5589_MAX_REG ADP5589_INT_EN -- GitLab From adf4932bc97ec9363dc5c0f8390ee5caccf0f41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:06 +0100 Subject: [PATCH 341/868] dt-bindings: mfd: adp5585: add properties for input events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add properties related to input events. These devices can act as keyboards and can support events either via a keymap Matrix or through GPIs. Note that the device needs to be an interrupt controller for GPIs based events. We specifically need a property specifying the pins used by the keymap matrix since these devices have no requirement for rows and columns to be contiguous without holes which is enforced by the standard input properties. Reviewed-by: "Rob Herring (Arm)" Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-11-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/adi,adp5585.yaml | 188 +++++++++++++++++- 1 file changed, 186 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml index 9471af28419d8..b3bf2ed586104 100644 --- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml +++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml @@ -49,6 +49,84 @@ properties: "#pwm-cells": const: 3 + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + poll-interval: + enum: [10, 20, 30, 40] + default: 10 + + adi,keypad-pins: + description: Specifies the pins used for the keypad matrix. + $ref: /schemas/types.yaml#/definitions/uint32-array + + adi,unlock-events: + description: + Specifies a maximum of 2 events that can be used to unlock the keypad. + If this property is set, the keyboard will be locked and only unlocked + after these keys/gpis are pressed. The value 127 serves as a wildcard which + means any key can be used for unlocking. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 2 + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 + - const: 127 + + adi,unlock-trigger-sec: + description: + Defines the time in which the second unlock event must occur after the + first unlock event has occurred. + maximum: 7 + default: 0 + + adi,reset1-events: + description: + Defines the trigger events (key/gpi presses) that can generate reset + conditions one the reset1 block. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 3 + + adi,reset2-events: + description: + Defines the trigger events (key/gpi presses) that can generate reset + conditions one the reset2 block. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 2 + + adi,reset1-active-high: + description: Sets the reset1 signal as active high. + type: boolean + + adi,reset2-active-high: + description: Sets the reset2 signal as active high. + type: boolean + + adi,rst-passthrough-enable: + description: Allows the RST pin to override (OR with) the reset1 signal. + type: boolean + + adi,reset-trigger-ms: + description: + Defines the length of time that the reset events must be active before a + reset signal is generated. All events must be active at the same time for + the same duration. + enum: [0, 1000, 1500, 2000, 2500, 3000, 3500, 4000] + default: 0 + + adi,reset-pulse-width-us: + description: Defines the pulse width of the reset signals. + enum: [500, 1000, 2000, 10000] + default: 500 + patternProperties: "-hog(-[0-9]+)?$": type: object @@ -56,11 +134,28 @@ patternProperties: required: - gpio-hog +dependencies: + linux,keymap: + - adi,keypad-pins + - interrupts + interrupt-controller: + - interrupts + adi,unlock-trigger-sec: + - adi,unlock-events + adi,reset1-active-high: + - adi,reset1-events + adi,rst-passtrough-enable: + - adi,reset1-events + adi,reset2-active-high: + - adi,reset2-events + required: - compatible - reg allOf: + - $ref: /schemas/input/matrix-keymap.yaml# + - $ref: /schemas/input/input.yaml# - if: properties: compatible: @@ -68,8 +163,29 @@ allOf: const: adi,adp5585-01 then: properties: + adi,unlock-events: false + adi,unlock-trigger-sec: false gpio-reserved-ranges: false - + adi,keypad-pins: + minItems: 2 + maxItems: 11 + items: + minimum: 0 + maximum: 10 + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 30 + - minimum: 37 + maximum: 47 + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 30 + - minimum: 37 + maximum: 47 - if: properties: compatible: @@ -81,6 +197,25 @@ allOf: - adi,adp5585-04 then: properties: + adi,unlock-events: false + adi,unlock-trigger-sec: false + adi,keypad-pins: + minItems: 2 + maxItems: 10 + items: + enum: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10] + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 25 + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47] + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 25 + - enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47] gpio-reserved-ranges: maxItems: 1 items: @@ -99,11 +234,33 @@ allOf: then: properties: gpio-reserved-ranges: false + adi,keypad-pins: + minItems: 2 + maxItems: 19 + items: + minimum: 0 + maximum: 18 + adi,reset1-events: + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 + adi,reset2-events: + items: + anyOf: + - minimum: 1 + maximum: 88 + - minimum: 97 + maximum: 115 -additionalProperties: false +unevaluatedProperties: false examples: - | + #include + #include i2c { #address-cells = <1>; #size-cells = <0>; @@ -119,6 +276,33 @@ examples: gpio-reserved-ranges = <5 1>; #pwm-cells = <3>; + + interrupts = <16 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + + adi,reset1-events = <1 43>; + adi,reset2-events = <2 3>; + adi,reset-trigger-ms = <2000>; + + /* + * col0, col1, col2 + * row0, row1, row2 + */ + adi,keypad-pins = <0 1 2 6 7 8>; + + linux,keymap = < + MATRIX_KEY(0x00, 0x00, KEY_1) + MATRIX_KEY(0x00, 0x01, KEY_2) + MATRIX_KEY(0x00, 0x02, KEY_3) + + MATRIX_KEY(0x01, 0x00, KEY_A) + MATRIX_KEY(0x01, 0x01, KEY_B) + MATRIX_KEY(0x01, 0x02, KEY_C) + + MATRIX_KEY(0x02, 0x00, BTN_1) + MATRIX_KEY(0x02, 0x01, BTN_2) + MATRIX_KEY(0x02, 0x02, BTN_3) + >; }; }; -- GitLab From 47a1f759b776ec9287f675f5d4fbf60b94cc566d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:07 +0100 Subject: [PATCH 342/868] mfd: adp5585: Add support for event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These devices are capable of generate FIFO based events based on KEY or GPI presses. Add support for handling these events. This is in preparation of adding full support for keymap and gpis based events. Reviewed-by: Lee Jones Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-12-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 176 ++++++++++++++++++++++++++++++++++-- include/linux/mfd/adp5585.h | 18 ++++ 2 files changed, 186 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index ae12372bdde91..ae2448697ef48 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -165,10 +166,16 @@ static const struct regmap_config adp5589_regmap_config_template = { static const struct adp5585_regs adp5585_regs = { .ext_cfg = ADP5585_PIN_CONFIG_C, + .int_en = ADP5585_INT_EN, + .gen_cfg = ADP5585_GENERAL_CFG, + .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG, }; static const struct adp5585_regs adp5589_regs = { .ext_cfg = ADP5589_PIN_CONFIG_D, + .int_en = ADP5589_INT_EN, + .gen_cfg = ADP5589_GENERAL_CFG, + .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG, }; static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585) @@ -241,6 +248,146 @@ static void adp5585_osc_disable(void *data) regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0); } +static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt) +{ + unsigned int i; + + for (i = 0; i < ev_cnt; i++) { + unsigned long key_val, key_press; + unsigned int key; + int ret; + + ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key); + if (ret) + return; + + key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key); + key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key); + + blocking_notifier_call_chain(&adp5585->event_notifier, key_val, (void *)key_press); + } +} + +static irqreturn_t adp5585_irq(int irq, void *data) +{ + struct adp5585_dev *adp5585 = data; + unsigned int status, ev_cnt; + int ret; + + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status); + if (ret) + return IRQ_HANDLED; + + if (status & ADP5585_OVRFLOW_INT) + dev_err_ratelimited(adp5585->dev, "Event overflow error\n"); + + if (!(status & ADP5585_EVENT_INT)) + goto out_irq; + + ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt); + if (ret) + goto out_irq; + + ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt); + if (!ev_cnt) + goto out_irq; + + adp5585_report_events(adp5585, ev_cnt); +out_irq: + regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status); + return IRQ_HANDLED; +} + +static int adp5585_setup(struct adp5585_dev *adp5585) +{ + const struct adp5585_regs *regs = adp5585->regs; + unsigned int reg_val, i; + int ret; + + /* Clear any possible event by reading all the FIFO entries */ + for (i = 0; i < ADP5585_EV_MAX; i++) { + ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val); + if (ret) + return ret; + } + + ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg, adp5585->ev_poll_time); + if (ret) + return ret; + + /* + * Enable the internal oscillator, as it's shared between multiple + * functions. + */ + ret = regmap_write(adp5585->regmap, regs->gen_cfg, + ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG | ADP5585_OSC_EN); + if (ret) + return ret; + + return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable, adp5585); +} + +static int adp5585_parse_fw(struct adp5585_dev *adp5585) +{ + unsigned int prop_val; + int ret; + + ret = device_property_read_u32(adp5585->dev, "poll-interval", &prop_val); + if (!ret) { + adp5585->ev_poll_time = prop_val / 10 - 1; + /* + * ev_poll_time is the raw value to be written on the register and 0 to 3 are the + * valid values. + */ + if (adp5585->ev_poll_time > 3) + return dev_err_probe(adp5585->dev, -EINVAL, + "Invalid value(%u) for poll-interval\n", prop_val); + } + + return 0; +} + +static void adp5585_irq_disable(void *data) +{ + struct adp5585_dev *adp5585 = data; + + regmap_write(adp5585->regmap, adp5585->regs->int_en, 0); +} + +static int adp5585_irq_enable(struct i2c_client *i2c, + struct adp5585_dev *adp5585) +{ + const struct adp5585_regs *regs = adp5585->regs; + unsigned int stat; + int ret; + + if (i2c->irq <= 0) + return 0; + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq, + IRQF_ONESHOT, i2c->name, adp5585); + if (ret) + return ret; + + /* + * Clear any possible outstanding interrupt before enabling them. We do that by reading + * the status register and writing back the same value. + */ + ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat); + if (ret) + return ret; + + ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat); + if (ret) + return ret; + + ret = regmap_write(adp5585->regmap, regs->int_en, ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN); + if (ret) + return ret; + + return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable, adp5585); +} + static int adp5585_i2c_probe(struct i2c_client *i2c) { struct regmap_config *regmap_config; @@ -254,6 +401,8 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, adp5585); adp5585->dev = &i2c->dev; + adp5585->irq = i2c->irq; + BLOCKING_INIT_NOTIFIER_HEAD(&adp5585->event_notifier); adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c); if (!adp5585->variant) @@ -278,25 +427,28 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, -ENODEV, "Invalid device ID 0x%02x\n", id); - /* - * Enable the internal oscillator, as it's shared between multiple - * functions. - */ - ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN); + ret = adp5585_parse_fw(adp5585); if (ret) return ret; - ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585); + ret = adp5585_setup(adp5585); if (ret) return ret; - return adp5585_add_devices(adp5585); + ret = adp5585_add_devices(adp5585); + if (ret) + return ret; + + return adp5585_irq_enable(i2c, adp5585); } static int adp5585_suspend(struct device *dev) { struct adp5585_dev *adp5585 = dev_get_drvdata(dev); + if (adp5585->irq) + disable_irq(adp5585->irq); + regcache_cache_only(adp5585->regmap, true); return 0; @@ -305,11 +457,19 @@ static int adp5585_suspend(struct device *dev) static int adp5585_resume(struct device *dev) { struct adp5585_dev *adp5585 = dev_get_drvdata(dev); + int ret; regcache_cache_only(adp5585->regmap, false); regcache_mark_dirty(adp5585->regmap); - return regcache_sync(adp5585->regmap); + ret = regcache_sync(adp5585->regmap); + if (ret) + return ret; + + if (adp5585->irq) + enable_irq(adp5585->irq); + + return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume); diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 77f7c74f084dd..43a33a3d3f5a1 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -10,13 +10,20 @@ #define __MFD_ADP5585_H_ #include +#include #define ADP5585_ID 0x00 #define ADP5585_MAN_ID_VALUE 0x20 #define ADP5585_MAN_ID_MASK GENMASK(7, 4) +#define ADP5585_REV_ID_MASK GENMASK(3, 0) #define ADP5585_INT_STATUS 0x01 +#define ADP5585_OVRFLOW_INT BIT(2) +#define ADP5585_EVENT_INT BIT(0) #define ADP5585_STATUS 0x02 +#define ADP5585_EC_MASK GENMASK(4, 0) #define ADP5585_FIFO_1 0x03 +#define ADP5585_KEV_EV_PRESS_MASK BIT(7) +#define ADP5585_KEY_EVENT_MASK GENMASK(6, 0) #define ADP5585_FIFO_2 0x04 #define ADP5585_FIFO_3 0x05 #define ADP5585_FIFO_4 0x06 @@ -32,6 +39,7 @@ #define ADP5585_FIFO_14 0x10 #define ADP5585_FIFO_15 0x11 #define ADP5585_FIFO_16 0x12 +#define ADP5585_EV_MAX (ADP5585_FIFO_16 - ADP5585_FIFO_1 + 1) #define ADP5585_GPI_INT_STAT_A 0x13 #define ADP5585_GPI_INT_STAT_B 0x14 #define ADP5585_GPI_STATUS_A 0x15 @@ -104,6 +112,8 @@ #define ADP5585_INT_CFG BIT(1) #define ADP5585_RST_CFG BIT(0) #define ADP5585_INT_EN 0x3c +#define ADP5585_OVRFLOW_IEN BIT(2) +#define ADP5585_EVENT_IEN BIT(0) #define ADP5585_MAX_REG ADP5585_INT_EN @@ -121,7 +131,9 @@ #define ADP5589_PWM_OFFT_LOW 0x3e #define ADP5589_PWM_ONT_LOW 0x40 #define ADP5589_PWM_CFG 0x42 +#define ADP5589_POLL_PTIME_CFG 0x48 #define ADP5589_PIN_CONFIG_D 0x4C +#define ADP5589_GENERAL_CFG 0x4d #define ADP5589_INT_EN 0x4e #define ADP5589_MAX_REG ADP5589_INT_EN @@ -142,15 +154,21 @@ enum adp5585_variant { }; struct adp5585_regs { + unsigned int gen_cfg; unsigned int ext_cfg; + unsigned int int_en; + unsigned int poll_ptime_cfg; }; struct adp5585_dev { struct device *dev; struct regmap *regmap; const struct adp5585_regs *regs; + struct blocking_notifier_head event_notifier; enum adp5585_variant variant; unsigned int id; + int irq; + unsigned int ev_poll_time; }; #endif -- GitLab From 333812da70d5f71bf5e176f6d55a5f716301b5fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:08 +0100 Subject: [PATCH 343/868] mfd: adp5585: Support reset and unlock events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADP558x family of devices can be programmed to respond to some especial events, In case of the unlock events, one can lock the keypad and use KEYS or GPIs events to unlock it. For the reset events, one can again use a combinations of GPIs/KEYs in order to generate an event that will trigger the device to generate an output reset pulse. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-13-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 274 +++++++++++++++++++++++++++++++++++- include/linux/mfd/adp5585.h | 40 ++++++ 2 files changed, 312 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index ae2448697ef48..30014deee41fa 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -169,6 +169,9 @@ static const struct adp5585_regs adp5585_regs = { .int_en = ADP5585_INT_EN, .gen_cfg = ADP5585_GENERAL_CFG, .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG, + .reset_cfg = ADP5585_RESET_CFG, + .reset1_event_a = ADP5585_RESET1_EVENT_A, + .reset2_event_a = ADP5585_RESET2_EVENT_A, }; static const struct adp5585_regs adp5589_regs = { @@ -176,8 +179,54 @@ static const struct adp5585_regs adp5589_regs = { .int_en = ADP5589_INT_EN, .gen_cfg = ADP5589_GENERAL_CFG, .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG, + .reset_cfg = ADP5589_RESET_CFG, + .reset1_event_a = ADP5589_RESET1_EVENT_A, + .reset2_event_a = ADP5589_RESET2_EVENT_A, }; +static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev) +{ + if (adp5585->has_pin6) { + if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <= ADP5585_ROW5_KEY_EVENT_END) + return 0; + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) + return 0; + + return dev_err_probe(adp5585->dev, -EINVAL, + "Invalid unlock/reset event(%u) for this device\n", ev); + } + + if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END) + return 0; + if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) { + /* + * Some variants of the adp5585 do not have the Row 5 + * (meaning pin 6 or GPIO 6) available. Instead that pin serves + * as a reset pin. So, we need to make sure no event is + * configured for it. + */ + if (ev == (ADP5585_GPI_EVENT_START + 5)) + return dev_err_probe(adp5585->dev, -EINVAL, + "Invalid unlock/reset event(%u). R5 not available\n", + ev); + return 0; + } + + return dev_err_probe(adp5585->dev, -EINVAL, + "Invalid unlock/reset event(%u) for this device\n", ev); +} + +static int adp5589_validate_event(const struct adp5585_dev *adp5585, unsigned int ev) +{ + if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END) + return 0; + if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END) + return 0; + + return dev_err_probe(adp5585->dev, -EINVAL, + "Invalid unlock/reset event(%u) for this device\n", ev); +} + static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585) { struct regmap_config *regmap_config; @@ -190,6 +239,8 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp case ADP5585_04: adp5585->id = ADP5585_MAN_ID_VALUE; adp5585->regs = &adp5585_regs; + if (adp5585->variant == ADP5585_01) + adp5585->has_pin6 = true; regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, sizeof(*regmap_config), GFP_KERNEL); break; @@ -198,6 +249,8 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp case ADP5589_02: adp5585->id = ADP5589_MAN_ID_VALUE; adp5585->regs = &adp5589_regs; + adp5585->has_unlock = true; + adp5585->has_pin6 = true; regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template, sizeof(*regmap_config), GFP_KERNEL); break; @@ -213,6 +266,167 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp return regmap_config; } +static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const char *prop, u32 *events, + u32 *n_events, u32 max_evs, bool reset_ev) +{ + struct device *dev = adp5585->dev; + unsigned int ev; + int ret; + + /* + * The device has the capability of handling special events through GPIs or a Keypad: + * unlock events: Unlock the keymap until one of the configured events is detected. + * reset events: Generate a reset pulse when one of the configured events is detected. + */ + ret = device_property_count_u32(dev, prop); + if (ret < 0) + return 0; + + *n_events = ret; + + if (!adp5585->has_unlock && !reset_ev) + return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not supported\n"); + + if (*n_events > max_evs) + return dev_err_probe(dev, -EINVAL, + "Invalid number of keys(%u > %u) for %s\n", + *n_events, max_evs, prop); + + ret = device_property_read_u32_array(dev, prop, events, *n_events); + if (ret) + return ret; + + for (ev = 0; ev < *n_events; ev++) { + if (!reset_ev && events[ev] == ADP5589_UNLOCK_WILDCARD) + continue; + + if (adp5585->id == ADP5585_MAN_ID_VALUE) + ret = adp5585_validate_event(adp5585, events[ev]); + else + ret = adp5589_validate_event(adp5585, events[ev]); + if (ret) + return ret; + } + + return 0; +} + +static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585) +{ + struct device *dev = adp5585->dev; + int ret; + + ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585->unlock_keys, + &adp5585->nkeys_unlock, ARRAY_SIZE(adp5585->unlock_keys), + false); + if (ret) + return ret; + if (!adp5585->nkeys_unlock) + return 0; + + ret = device_property_read_u32(dev, "adi,unlock-trigger-sec", &adp5585->unlock_time); + if (!ret) { + if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC) + return dev_err_probe(dev, -EINVAL, + "Invalid unlock time(%u > %d)\n", + adp5585->unlock_time, + ADP5585_MAX_UNLOCK_TIME_SEC); + } + + return 0; +} + +static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585) +{ + struct device *dev = adp5585->dev; + u32 prop_val; + int ret; + + ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585->reset1_keys, + &adp5585->nkeys_reset1, + ARRAY_SIZE(adp5585->reset1_keys), true); + if (ret) + return ret; + + ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events", + adp5585->reset2_keys, + &adp5585->nkeys_reset2, + ARRAY_SIZE(adp5585->reset2_keys), true); + if (ret) + return ret; + + if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2) + return 0; + + if (adp5585->nkeys_reset1 && device_property_read_bool(dev, "adi,reset1-active-high")) + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1); + + if (adp5585->nkeys_reset2 && device_property_read_bool(dev, "adi,reset2-active-high")) + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1); + + if (device_property_read_bool(dev, "adi,rst-passthrough-enable")) + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN, 1); + + ret = device_property_read_u32(dev, "adi,reset-trigger-ms", &prop_val); + if (!ret) { + switch (prop_val) { + case 0: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0); + break; + case 1000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1); + break; + case 1500: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2); + break; + case 2000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3); + break; + case 2500: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4); + break; + case 3000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5); + break; + case 3500: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6); + break; + case 4000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7); + break; + default: + return dev_err_probe(dev, -EINVAL, + "Invalid value(%u) for adi,reset-trigger-ms\n", + prop_val); + } + } + + ret = device_property_read_u32(dev, "adi,reset-pulse-width-us", &prop_val); + if (!ret) { + switch (prop_val) { + case 500: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 0); + break; + case 1000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 1); + break; + case 2000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 2); + break; + case 10000: + adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 3); + break; + default: + return dev_err_probe(dev, -EINVAL, + "Invalid value(%u) for adi,reset-pulse-width-us\n", + prop_val); + } + return ret; + } + + return 0; +} + static int adp5585_add_devices(const struct adp5585_dev *adp5585) { struct device *dev = adp5585->dev; @@ -301,9 +515,61 @@ out_irq: static int adp5585_setup(struct adp5585_dev *adp5585) { const struct adp5585_regs *regs = adp5585->regs; - unsigned int reg_val, i; + unsigned int reg_val = 0, i; int ret; + /* Configure the device with reset and unlock events */ + for (i = 0; i < adp5585->nkeys_unlock; i++) { + ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i, + adp5585->unlock_keys[i] | ADP5589_UNLOCK_EV_PRESS); + if (ret) + return ret; + } + + if (adp5585->nkeys_unlock) { + ret = regmap_update_bits(adp5585->regmap, ADP5589_UNLOCK_TIMERS, + ADP5589_UNLOCK_TIMER, adp5585->unlock_time); + if (ret) + return ret; + + ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG, ADP5589_LOCK_EN); + if (ret) + return ret; + } + + for (i = 0; i < adp5585->nkeys_reset1; i++) { + ret = regmap_write(adp5585->regmap, regs->reset1_event_a + i, + adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS); + if (ret) + return ret; + } + + for (i = 0; i < adp5585->nkeys_reset2; i++) { + ret = regmap_write(adp5585->regmap, regs->reset2_event_a + i, + adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS); + if (ret) + return ret; + } + + if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) { + ret = regmap_write(adp5585->regmap, regs->reset_cfg, adp5585->reset_cfg); + if (ret) + return ret; + + /* If there's a reset1 event, then R4 is used as an output for the reset signal */ + if (adp5585->nkeys_reset1) + reg_val = ADP5585_R4_EXTEND_CFG_RESET1; + /* If there's a reset2 event, then C4 is used as an output for the reset signal */ + if (adp5585->nkeys_reset2) + reg_val |= ADP5585_C4_EXTEND_CFG_RESET2; + + ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg, + ADP5585_C4_EXTEND_CFG_MASK | ADP5585_R4_EXTEND_CFG_MASK, + reg_val); + if (ret) + return ret; + } + /* Clear any possible event by reading all the FIFO entries */ for (i = 0; i < ADP5585_EV_MAX; i++) { ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val); @@ -344,7 +610,11 @@ static int adp5585_parse_fw(struct adp5585_dev *adp5585) "Invalid value(%u) for poll-interval\n", prop_val); } - return 0; + ret = adp5585_unlock_ev_parse(adp5585); + if (ret) + return ret; + + return adp5585_reset_ev_parse(adp5585); } static void adp5585_irq_disable(void *data) diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 43a33a3d3f5a1..db483ef9693a4 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -68,6 +68,7 @@ #define ADP5585_GPIO_DIRECTION_A 0x27 #define ADP5585_GPIO_DIRECTION_B 0x28 #define ADP5585_RESET1_EVENT_A 0x29 +#define ADP5585_RESET_EV_PRESS BIT(7) #define ADP5585_RESET1_EVENT_B 0x2a #define ADP5585_RESET1_EVENT_C 0x2b #define ADP5585_RESET2_EVENT_A 0x2c @@ -118,6 +119,13 @@ #define ADP5585_MAX_REG ADP5585_INT_EN #define ADP5585_PIN_MAX 11 +#define ADP5585_MAX_UNLOCK_TIME_SEC 7 +#define ADP5585_KEY_EVENT_START 1 +#define ADP5585_KEY_EVENT_END 25 +#define ADP5585_GPI_EVENT_START 37 +#define ADP5585_GPI_EVENT_END 47 +#define ADP5585_ROW5_KEY_EVENT_START 1 +#define ADP5585_ROW5_KEY_EVENT_END 30 /* ADP5589 */ #define ADP5589_MAN_ID_VALUE 0x10 @@ -128,6 +136,20 @@ #define ADP5589_GPO_DATA_OUT_A 0x2a #define ADP5589_GPO_OUT_MODE_A 0x2d #define ADP5589_GPIO_DIRECTION_A 0x30 +#define ADP5589_UNLOCK1 0x33 +#define ADP5589_UNLOCK_EV_PRESS BIT(7) +#define ADP5589_UNLOCK_TIMERS 0x36 +#define ADP5589_UNLOCK_TIMER GENMASK(2, 0) +#define ADP5589_LOCK_CFG 0x37 +#define ADP5589_LOCK_EN BIT(0) +#define ADP5589_RESET1_EVENT_A 0x38 +#define ADP5589_RESET2_EVENT_A 0x3B +#define ADP5589_RESET_CFG 0x3D +#define ADP5585_RESET2_POL BIT(7) +#define ADP5585_RESET1_POL BIT(6) +#define ADP5585_RST_PASSTHRU_EN BIT(5) +#define ADP5585_RESET_TRIG_TIME GENMASK(4, 2) +#define ADP5585_PULSE_WIDTH GENMASK(1, 0) #define ADP5589_PWM_OFFT_LOW 0x3e #define ADP5589_PWM_ONT_LOW 0x40 #define ADP5589_PWM_CFG 0x42 @@ -138,6 +160,11 @@ #define ADP5589_MAX_REG ADP5589_INT_EN #define ADP5589_PIN_MAX 19 +#define ADP5589_KEY_EVENT_START 1 +#define ADP5589_KEY_EVENT_END 88 +#define ADP5589_GPI_EVENT_START 97 +#define ADP5589_GPI_EVENT_END 115 +#define ADP5589_UNLOCK_WILDCARD 127 struct regmap; @@ -158,6 +185,9 @@ struct adp5585_regs { unsigned int ext_cfg; unsigned int int_en; unsigned int poll_ptime_cfg; + unsigned int reset_cfg; + unsigned int reset1_event_a; + unsigned int reset2_event_a; }; struct adp5585_dev { @@ -167,8 +197,18 @@ struct adp5585_dev { struct blocking_notifier_head event_notifier; enum adp5585_variant variant; unsigned int id; + bool has_unlock; + bool has_pin6; int irq; unsigned int ev_poll_time; + unsigned int unlock_time; + unsigned int unlock_keys[2]; + unsigned int nkeys_unlock; + unsigned int reset1_keys[3]; + unsigned int nkeys_reset1; + unsigned int reset2_keys[2]; + unsigned int nkeys_reset2; + u8 reset_cfg; }; #endif -- GitLab From bd113a13e1fa51789f55987369b80e1d8bc19389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:09 +0100 Subject: [PATCH 344/868] mfd: adp5585: Add support for input devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADP558x family supports a built in keypad matrix decoder which can be added as an Input device. In order to both support the Input and the GPIO device, we need to create a bitmap of the supported pins and track their usage since they can either be used as GPIOs (GPIs) or as part of the keymap. We also need to mark special pins busy in case some features are being used (ex: pwm or reset events). Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-14-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 31 +++++++++++++++++++++++++++++++ include/linux/mfd/adp5585.h | 10 ++++++++++ 2 files changed, 41 insertions(+) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 30014deee41fa..8f0fd73744261 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -22,17 +22,20 @@ enum { ADP5585_DEV_GPIO, ADP5585_DEV_PWM, + ADP5585_DEV_INPUT, ADP5585_DEV_MAX }; static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = { MFD_CELL_NAME("adp5585-gpio"), MFD_CELL_NAME("adp5585-pwm"), + MFD_CELL_NAME("adp5585-keys"), }; static const struct mfd_cell adp5589_devs[] = { MFD_CELL_NAME("adp5589-gpio"), MFD_CELL_NAME("adp5589-pwm"), + MFD_CELL_NAME("adp5589-keys"), }; static const struct regmap_range adp5585_volatile_ranges[] = { @@ -172,6 +175,7 @@ static const struct adp5585_regs adp5585_regs = { .reset_cfg = ADP5585_RESET_CFG, .reset1_event_a = ADP5585_RESET1_EVENT_A, .reset2_event_a = ADP5585_RESET2_EVENT_A, + .pin_cfg_a = ADP5585_PIN_CONFIG_A, }; static const struct adp5585_regs adp5589_regs = { @@ -182,6 +186,7 @@ static const struct adp5585_regs adp5589_regs = { .reset_cfg = ADP5589_RESET_CFG, .reset1_event_a = ADP5589_RESET1_EVENT_A, .reset2_event_a = ADP5589_RESET2_EVENT_A, + .pin_cfg_a = ADP5589_PIN_CONFIG_A, }; static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev) @@ -239,6 +244,8 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp case ADP5585_04: adp5585->id = ADP5585_MAN_ID_VALUE; adp5585->regs = &adp5585_regs; + adp5585->n_pins = ADP5585_PIN_MAX; + adp5585->reset2_out = ADP5585_RESET2_OUT; if (adp5585->variant == ADP5585_01) adp5585->has_pin6 = true; regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template, @@ -251,6 +258,8 @@ static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp adp5585->regs = &adp5589_regs; adp5585->has_unlock = true; adp5585->has_pin6 = true; + adp5585->n_pins = ADP5589_PIN_MAX; + adp5585->reset2_out = ADP5589_RESET2_OUT; regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template, sizeof(*regmap_config), GFP_KERNEL); break; @@ -439,6 +448,8 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585) cells = adp5589_devs; if (device_property_present(dev, "#pwm-cells")) { + /* Make sure the PWM output pin is not used by the GPIO or INPUT devices */ + __set_bit(ADP5585_PWM_OUT, adp5585->pin_usage); ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, &cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL); if (ret) @@ -452,6 +463,13 @@ static int adp5585_add_devices(const struct adp5585_dev *adp5585) return dev_err_probe(dev, ret, "Failed to add GPIO device\n"); } + if (device_property_present(adp5585->dev, "adi,keypad-pins")) { + ret = devm_mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO, + &cells[ADP5585_DEV_INPUT], 1, NULL, 0, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to add input device\n"); + } + return 0; } @@ -518,6 +536,10 @@ static int adp5585_setup(struct adp5585_dev *adp5585) unsigned int reg_val = 0, i; int ret; + /* If pin_6 (ROW5/GPI6) is not available, make sure to mark it as "busy" */ + if (!adp5585->has_pin6) + __set_bit(ADP5585_ROW5, adp5585->pin_usage); + /* Configure the device with reset and unlock events */ for (i = 0; i < adp5585->nkeys_unlock; i++) { ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i, @@ -542,6 +564,9 @@ static int adp5585_setup(struct adp5585_dev *adp5585) adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS); if (ret) return ret; + + /* Mark that pin as not usable for the INPUT and GPIO devices. */ + __set_bit(ADP5585_RESET1_OUT, adp5585->pin_usage); } for (i = 0; i < adp5585->nkeys_reset2; i++) { @@ -549,6 +574,8 @@ static int adp5585_setup(struct adp5585_dev *adp5585) adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS); if (ret) return ret; + + __set_bit(adp5585->reset2_out, adp5585->pin_usage); } if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) { @@ -697,6 +724,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, -ENODEV, "Invalid device ID 0x%02x\n", id); + adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->n_pins, GFP_KERNEL); + if (!adp5585->pin_usage) + return -ENOMEM; + ret = adp5585_parse_fw(adp5585); if (ret) return ret; diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index db483ef9693a4..41c5d2e1cc7ca 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -126,6 +126,10 @@ #define ADP5585_GPI_EVENT_END 47 #define ADP5585_ROW5_KEY_EVENT_START 1 #define ADP5585_ROW5_KEY_EVENT_END 30 +#define ADP5585_PWM_OUT 3 +#define ADP5585_RESET1_OUT 4 +#define ADP5585_RESET2_OUT 9 +#define ADP5585_ROW5 5 /* ADP5589 */ #define ADP5589_MAN_ID_VALUE 0x10 @@ -154,6 +158,7 @@ #define ADP5589_PWM_ONT_LOW 0x40 #define ADP5589_PWM_CFG 0x42 #define ADP5589_POLL_PTIME_CFG 0x48 +#define ADP5589_PIN_CONFIG_A 0x49 #define ADP5589_PIN_CONFIG_D 0x4C #define ADP5589_GENERAL_CFG 0x4d #define ADP5589_INT_EN 0x4e @@ -165,6 +170,7 @@ #define ADP5589_GPI_EVENT_START 97 #define ADP5589_GPI_EVENT_END 115 #define ADP5589_UNLOCK_WILDCARD 127 +#define ADP5589_RESET2_OUT 12 struct regmap; @@ -188,6 +194,7 @@ struct adp5585_regs { unsigned int reset_cfg; unsigned int reset1_event_a; unsigned int reset2_event_a; + unsigned int pin_cfg_a; }; struct adp5585_dev { @@ -195,6 +202,9 @@ struct adp5585_dev { struct regmap *regmap; const struct adp5585_regs *regs; struct blocking_notifier_head event_notifier; + unsigned long *pin_usage; + unsigned int n_pins; + unsigned int reset2_out; enum adp5585_variant variant; unsigned int id; bool has_unlock; -- GitLab From 988b28a83b658137e58123f4dafc3a1588e1cb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:10 +0100 Subject: [PATCH 345/868] gpio: adp5585: support gpi events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for adding GPIs to the event FIFO. This is done by adding irq_chip support. Like this, one can use the input gpio_keys driver as a "frontend" device and input handler. As part of this change, we now implement .request() and .free() as we can't blindly consume all available pins as GPIOs (example: some pins can be used for forming a keymap matrix). Also note that the number of pins can now be obtained from the parent, top level device. Hence the 'max_gpio' variable can be removed. Reviewed-by: Linus Walleij Acked-by: Bartosz Golaszewski Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-15-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-adp5585.c | 221 +++++++++++++++++++++++++++++++++++- include/linux/mfd/adp5585.h | 2 + 3 files changed, 220 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 44f922e10db2f..b552401e3f73f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1263,6 +1263,7 @@ config GPIO_ADP5520 config GPIO_ADP5585 tristate "GPIO Support for ADP5585" depends on MFD_ADP5585 + select GPIOLIB_IRQCHIP help This option enables support for the GPIO function found in the Analog Devices ADP5585. diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c index cdf107742579c..b2c8836c5f847 100644 --- a/drivers/gpio/gpio-adp5585.c +++ b/drivers/gpio/gpio-adp5585.c @@ -7,10 +7,15 @@ * Copyright 2025 Analog Devices, Inc. */ +#include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -36,20 +41,29 @@ struct adp5585_gpio_chip { int (*bank)(unsigned int off); int (*bit)(unsigned int off); - unsigned int max_gpio; unsigned int debounce_dis_a; unsigned int rpull_cfg_a; unsigned int gpo_data_a; unsigned int gpo_out_a; unsigned int gpio_dir_a; unsigned int gpi_stat_a; + unsigned int gpi_int_lvl_a; + unsigned int gpi_ev_a; + unsigned int gpi_ev_min; + unsigned int gpi_ev_max; bool has_bias_hole; }; struct adp5585_gpio_dev { struct gpio_chip gpio_chip; + struct notifier_block nb; const struct adp5585_gpio_chip *info; struct regmap *regmap; + unsigned long irq_mask; + unsigned long irq_en; + unsigned long irq_active_high; + /* used for irqchip bus locking */ + struct mutex bus_lock; }; static int adp5585_gpio_bank(unsigned int off) @@ -224,12 +238,175 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off, }; } +static int adp5585_gpio_request(struct gpio_chip *chip, unsigned int off) +{ + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + struct device *dev = chip->parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + const struct adp5585_regs *regs = adp5585->regs; + int ret; + + ret = test_and_set_bit(off, adp5585->pin_usage); + if (ret) + return -EBUSY; + + /* make sure it's configured for GPIO */ + return regmap_clear_bits(adp5585_gpio->regmap, + regs->pin_cfg_a + info->bank(off), + info->bit(off)); +} + +static void adp5585_gpio_free(struct gpio_chip *chip, unsigned int off) +{ + struct device *dev = chip->parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + + clear_bit(off, adp5585->pin_usage); +} + +static int adp5585_gpio_key_event(struct notifier_block *nb, unsigned long key, + void *data) +{ + struct adp5585_gpio_dev *adp5585_gpio = container_of(nb, struct adp5585_gpio_dev, nb); + struct device *dev = adp5585_gpio->gpio_chip.parent; + unsigned long key_press = (unsigned long)data; + unsigned int irq, irq_type; + struct irq_data *irqd; + bool active_high; + unsigned int off; + + /* make sure the event is for me */ + if (key < adp5585_gpio->info->gpi_ev_min || key > adp5585_gpio->info->gpi_ev_max) + return NOTIFY_DONE; + + off = key - adp5585_gpio->info->gpi_ev_min; + active_high = test_bit(off, &adp5585_gpio->irq_active_high); + + irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off); + if (!irq) + return NOTIFY_BAD; + + irqd = irq_get_irq_data(irq); + if (!irqd) { + dev_err(dev, "Could not get irq(%u) data\n", irq); + return NOTIFY_BAD; + } + + dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%lu, a_high=%u\n", + off, key_press, active_high); + + if (!active_high) + key_press = !key_press; + + irq_type = irqd_get_trigger_type(irqd); + + if ((irq_type & IRQ_TYPE_EDGE_RISING && key_press) || + (irq_type & IRQ_TYPE_EDGE_FALLING && !key_press)) + handle_nested_irq(irq); + + return NOTIFY_STOP; +} + +static void adp5585_irq_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + + mutex_lock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip); + const struct adp5585_gpio_chip *info = adp5585_gpio->info; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + bool active_high = test_bit(hwirq, &adp5585_gpio->irq_active_high); + bool enabled = test_bit(hwirq, &adp5585_gpio->irq_en); + bool masked = test_bit(hwirq, &adp5585_gpio->irq_mask); + unsigned int bank = adp5585_gpio->info->bank(hwirq); + unsigned int bit = adp5585_gpio->info->bit(hwirq); + + if (masked && !enabled) + goto out_unlock; + if (!masked && enabled) + goto out_unlock; + + regmap_update_bits(adp5585_gpio->regmap, info->gpi_int_lvl_a + bank, bit, + active_high ? bit : 0); + regmap_update_bits(adp5585_gpio->regmap, info->gpi_ev_a + bank, bit, + masked ? 0 : bit); + assign_bit(hwirq, &adp5585_gpio->irq_en, !masked); + +out_unlock: + mutex_unlock(&adp5585_gpio->bus_lock); +} + +static void adp5585_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + __set_bit(hwirq, &adp5585_gpio->irq_mask); + gpiochip_disable_irq(gc, hwirq); +} + +static void adp5585_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + __clear_bit(hwirq, &adp5585_gpio->irq_mask); +} + +static int adp5585_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + if (!(type & IRQ_TYPE_EDGE_BOTH)) + return -EINVAL; + + assign_bit(hwirq, &adp5585_gpio->irq_active_high, + type == IRQ_TYPE_EDGE_RISING); + + irq_set_handler_locked(d, handle_edge_irq); + return 0; +} + +static const struct irq_chip adp5585_irq_chip = { + .name = "adp5585", + .irq_mask = adp5585_irq_mask, + .irq_unmask = adp5585_irq_unmask, + .irq_bus_lock = adp5585_irq_bus_lock, + .irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock, + .irq_set_type = adp5585_irq_set_type, + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void adp5585_gpio_unreg_notifier(void *data) +{ + struct adp5585_gpio_dev *adp5585_gpio = data; + struct device *dev = adp5585_gpio->gpio_chip.parent; + struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent); + + blocking_notifier_chain_unregister(&adp5585->event_notifier, + &adp5585_gpio->nb); +} + static int adp5585_gpio_probe(struct platform_device *pdev) { struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); const struct platform_device_id *id = platform_get_device_id(pdev); struct adp5585_gpio_dev *adp5585_gpio; struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; struct gpio_chip *gc; int ret; @@ -253,13 +430,43 @@ static int adp5585_gpio_probe(struct platform_device *pdev) gc->get = adp5585_gpio_get_value; gc->set_rv = adp5585_gpio_set_value; gc->set_config = adp5585_gpio_set_config; + gc->request = adp5585_gpio_request; + gc->free = adp5585_gpio_free; gc->can_sleep = true; gc->base = -1; - gc->ngpio = adp5585_gpio->info->max_gpio; + gc->ngpio = adp5585->n_pins; gc->label = pdev->name; gc->owner = THIS_MODULE; + if (device_property_present(dev->parent, "interrupt-controller")) { + if (!adp5585->irq) + return dev_err_probe(dev, -EINVAL, + "Unable to serve as interrupt controller without IRQ\n"); + + girq = &adp5585_gpio->gpio_chip.irq; + gpio_irq_chip_set_chip(girq, &adp5585_irq_chip); + girq->handler = handle_bad_irq; + girq->threaded = true; + + adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event; + ret = blocking_notifier_chain_register(&adp5585->event_notifier, + &adp5585_gpio->nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier, + adp5585_gpio); + if (ret) + return ret; + } + + /* everything masked by default */ + adp5585_gpio->irq_mask = ~0UL; + + ret = devm_mutex_init(dev, &adp5585_gpio->bus_lock); + if (ret) + return ret; ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip, adp5585_gpio); if (ret) @@ -277,8 +484,11 @@ static const struct adp5585_gpio_chip adp5585_gpio_chip_info = { .gpo_out_a = ADP5585_GPO_OUT_MODE_A, .gpio_dir_a = ADP5585_GPIO_DIRECTION_A, .gpi_stat_a = ADP5585_GPI_STATUS_A, - .max_gpio = ADP5585_PIN_MAX, .has_bias_hole = true, + .gpi_ev_min = ADP5585_GPI_EVENT_START, + .gpi_ev_max = ADP5585_GPI_EVENT_END, + .gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A, + .gpi_ev_a = ADP5585_GPI_EVENT_EN_A, }; static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { @@ -290,7 +500,10 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = { .gpo_out_a = ADP5589_GPO_OUT_MODE_A, .gpio_dir_a = ADP5589_GPIO_DIRECTION_A, .gpi_stat_a = ADP5589_GPI_STATUS_A, - .max_gpio = ADP5589_PIN_MAX, + .gpi_ev_min = ADP5589_GPI_EVENT_START, + .gpi_ev_max = ADP5589_GPI_EVENT_END, + .gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A, + .gpi_ev_a = ADP5589_GPI_EVENT_EN_A, }; static const struct platform_device_id adp5585_gpio_id_table[] = { diff --git a/include/linux/mfd/adp5585.h b/include/linux/mfd/adp5585.h index 41c5d2e1cc7ca..5237da6b4a9f2 100644 --- a/include/linux/mfd/adp5585.h +++ b/include/linux/mfd/adp5585.h @@ -136,6 +136,8 @@ #define ADP5589_GPI_STATUS_A 0x16 #define ADP5589_GPI_STATUS_C 0x18 #define ADP5589_RPULL_CONFIG_A 0x19 +#define ADP5589_GPI_INT_LEVEL_A 0x1e +#define ADP5589_GPI_EVENT_EN_A 0x21 #define ADP5589_DEBOUNCE_DIS_A 0x27 #define ADP5589_GPO_DATA_OUT_A 0x2a #define ADP5589_GPO_OUT_MODE_A 0x2d -- GitLab From 19298ac01306e564b48df9aa239731cf967298d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:11 +0100 Subject: [PATCH 346/868] Input: adp5585: Add Analog Devices ADP5585/89 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADP5585 is a 10/11 input/output port expander with a built in keypad matrix decoder, programmable logic, reset generator, and PWM generator. This driver supports the keyboard function using the platform device registered by the core MFD driver. The ADP5589 has 19 pins and also features an unlock function. Acked-by: Dmitry Torokhov Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-16-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- MAINTAINERS | 1 + drivers/input/keyboard/Kconfig | 11 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/adp5585-keys.c | 371 ++++++++++++++++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 drivers/input/keyboard/adp5585-keys.c diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163..0d053c45f7f94 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -550,6 +550,7 @@ L: linux-pwm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml F: drivers/gpio/gpio-adp5585.c +F: drivers/input/keyboard/adp5585-keys.c F: drivers/mfd/adp5585.c F: drivers/pwm/pwm-adp5585.c F: include/linux/mfd/adp5585.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 721ab69e84ac6..e1f8cf2ac305d 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -37,6 +37,17 @@ config KEYBOARD_ADP5520 To compile this driver as a module, choose M here: the module will be called adp5520-keys. +config KEYBOARD_ADP5585 + tristate "ADP558x keypad support" + depends on MFD_ADP5585 + select INPUT_MATRIXKMAP + help + This option enables support for the KEYPAD function found in the Analog + Devices ADP5585 and similar devices. + + To compile this driver as a module, choose M here: the + module will be called adp5585-keys. + config KEYBOARD_ADP5588 tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander" depends on I2C diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1e0721c307096..f00ec003a59aa 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o +obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o diff --git a/drivers/input/keyboard/adp5585-keys.c b/drivers/input/keyboard/adp5585-keys.c new file mode 100644 index 0000000000000..4208229e13561 --- /dev/null +++ b/drivers/input/keyboard/adp5585-keys.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices ADP5585 Keys driver + * + * Copyright (C) 2025 Analog Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* As needed for the matrix parsing code */ +#define ADP5589_MAX_KEYMAPSIZE 123 + +struct adp5585_kpad_chip { + u8 key_ev_min; + u8 key_ev_max; + u8 max_rows; + u8 max_cols; +}; + +struct adp5585_kpad { + const struct adp5585_kpad_chip *info; + struct notifier_block nb; + struct input_dev *input; + unsigned short keycode[ADP5589_MAX_KEYMAPSIZE]; + struct device *dev; + unsigned long keypad; + int row_shift; +}; + +static int adp5585_keys_validate_events(const struct adp5585_kpad *kpad, + const u32 *events, u32 n_events) +{ + unsigned int ev; + u32 row, col; + + for (ev = 0; ev < n_events; ev++) { + if (events[ev] < kpad->info->key_ev_min || + events[ev] > kpad->info->key_ev_max) + continue; + + /* + * if the event is to be generated by the keymap, we need to make + * sure that the pins are part of it! + */ + row = (events[ev] - 1) / kpad->info->max_cols; + col = (events[ev] - 1) % kpad->info->max_cols; + + if (test_bit(row, &kpad->keypad) && + test_bit(col + kpad->info->max_rows, &kpad->keypad)) + continue; + + return dev_err_probe(kpad->dev, -EINVAL, + "Invalid unlock/reset event(%u) not used in the keypad\n", + events[ev]); + } + + return 0; +} + +static int adp5585_keys_check_special_events(const struct adp5585_dev *adp5585, + const struct adp5585_kpad *kpad) +{ + int error; + + error = adp5585_keys_validate_events(kpad, adp5585->unlock_keys, + adp5585->nkeys_unlock); + if (error) + return error; + + error = adp5585_keys_validate_events(kpad, adp5585->reset1_keys, + adp5585->nkeys_reset1); + if (error) + return error; + + return adp5585_keys_validate_events(kpad, adp5585->reset2_keys, + adp5585->nkeys_reset2); +} + +static void adp5585_keys_pins_free(void *data) +{ + struct adp5585_kpad *kpad = data; + struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent); + unsigned int pin; + + for_each_set_bit(pin, &kpad->keypad, adp5585->n_pins) + clear_bit(pin, adp5585->pin_usage); +} + +static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585, + struct adp5585_kpad *kpad) +{ + struct device *dev = kpad->dev; + u32 cols = 0, rows = 0, pin; + int error, n_pins; + + /* + * We do not check for errors (or no value) since the input device is + * only added if this property is present in the first place. + */ + n_pins = device_property_count_u32(dev, "adi,keypad-pins"); + if (n_pins > adp5585->n_pins) + return dev_err_probe(dev, -EINVAL, + "Too many keypad pins (%d) defined (max=%d)\n", + n_pins, adp5585->n_pins); + + unsigned int *keypad_pins __free(kfree) = kcalloc(n_pins, sizeof(*keypad_pins), + GFP_KERNEL); + if (!keypad_pins) + return -ENOMEM; + + error = device_property_read_u32_array(dev, "adi,keypad-pins", + keypad_pins, n_pins); + if (error) + return error; + + /* + * We can add the action here since it makes the code easier and nothing + * "bad" will happen out of it. Worst case, it will be a no-op and no + * bit will set. + */ + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad); + if (error) + return error; + + for (pin = 0; pin < n_pins; pin++) { + if (keypad_pins[pin] >= adp5585->n_pins) + return dev_err_probe(dev, -EINVAL, + "Invalid keypad pin(%u) defined\n", + keypad_pins[pin]); + + if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) + return dev_err_probe(dev, -EBUSY, + "Keypad pin(%u) already used\n", + keypad_pins[pin]); + + __set_bit(keypad_pins[pin], &kpad->keypad); + } + + /* + * Note that given that we get a mask (and the HW allows it), we + * can have holes in our keypad (eg: row0, row1 and row7 enabled). + * However, for the matrix parsing functions we need to pass the + * number of rows/cols as the maximum row/col used plus 1. This + * pretty much means we will also have holes in our SW keypad. + */ + + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; + if (rows == kpad->info->max_rows + 1) + return dev_err_probe(dev, -EINVAL, + "Now rows defined in the keypad!\n"); + + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad->info->max_rows); + if (cols < kpad->info->max_rows) + return dev_err_probe(dev, -EINVAL, + "No columns defined in the keypad!\n"); + + cols = cols + 1 - kpad->info->max_rows; + + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, + kpad->keycode, kpad->input); + if (error) + return error; + + kpad->row_shift = get_count_order(cols); + + if (device_property_read_bool(kpad->dev, "autorepeat")) + __set_bit(EV_REP, kpad->input->evbit); + + error = adp5585_keys_check_special_events(adp5585, kpad); + if (error) + return error; + + return 0; +} + +static int adp5585_keys_setup(const struct adp5585_dev *adp5585, + struct adp5585_kpad *kpad) +{ + unsigned long keys_bits, start = 0, nbits = kpad->info->max_rows; + const struct adp5585_regs *regs = adp5585->regs; + unsigned int i = 0, max_cols = kpad->info->max_cols; + int error; + + /* + * Take care as the below assumes max_rows is always less or equal than + * 8 which is true for the supported devices. If we happen to add + * another device we need to make sure this still holds true. Although + * adding a new device is very unlikely. + */ + do { + keys_bits = bitmap_read(&kpad->keypad, start, nbits); + if (keys_bits) { + error = regmap_write(adp5585->regmap, regs->pin_cfg_a + i, + keys_bits); + if (error) + return error; + } + + start += nbits; + if (max_cols > 8) { + nbits = 8; + max_cols -= nbits; + } else { + nbits = max_cols; + } + + i++; + } while (start < kpad->info->max_rows + kpad->info->max_cols); + + return 0; +} + +static int adp5585_keys_ev_handle(struct notifier_block *nb, unsigned long key, + void *data) +{ + struct adp5585_kpad *kpad = container_of(nb, struct adp5585_kpad, nb); + unsigned long key_press = (unsigned long)data; + unsigned int row, col, code; + + /* make sure the event is for us */ + if (key < kpad->info->key_ev_min || key > kpad->info->key_ev_max) + return NOTIFY_DONE; + + /* + * Unlikely but lets be on the safe side! We do not return any error + * because the event was indeed for us but with some weird value. So, + * we still want the caller know that the right handler was called. + */ + if (!key) + return NOTIFY_BAD; + + row = (key - 1) / (kpad->info->max_cols); + col = (key - 1) % (kpad->info->max_cols); + code = MATRIX_SCAN_CODE(row, col, kpad->row_shift); + + dev_dbg_ratelimited(kpad->dev, "report key(%lu) r(%d) c(%d) code(%d)\n", + key, row, col, kpad->keycode[code]); + + input_report_key(kpad->input, kpad->keycode[code], key_press); + input_sync(kpad->input); + + return NOTIFY_STOP; +} + +static void adp5585_keys_unreg_notifier(void *data) +{ + struct adp5585_kpad *kpad = data; + struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent); + + blocking_notifier_chain_unregister(&adp5585->event_notifier, + &kpad->nb); +} + +static int adp5585_keys_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct adp5585_kpad *kpad; + unsigned int revid; + const char *phys; + int error; + + kpad = devm_kzalloc(dev, sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + if (!adp5585->irq) + return dev_err_probe(dev, -EINVAL, + "IRQ is mandatory for the keypad\n"); + + kpad->dev = dev; + + kpad->input = devm_input_allocate_device(dev); + if (!kpad->input) + return -ENOMEM; + + kpad->info = (const struct adp5585_kpad_chip *)id->driver_data; + if (!kpad->info) + return -ENODEV; + + error = regmap_read(adp5585->regmap, ADP5585_ID, &revid); + if (error) + return dev_err_probe(dev, error, "Failed to read device ID\n"); + + phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", pdev->name); + if (!phys) + return -ENOMEM; + + kpad->input->name = pdev->name; + kpad->input->phys = phys; + + kpad->input->id.bustype = BUS_I2C; + kpad->input->id.vendor = 0x0001; + kpad->input->id.product = 0x0001; + kpad->input->id.version = revid & ADP5585_REV_ID_MASK; + + device_set_of_node_from_dev(dev, dev->parent); + + error = adp5585_keys_parse_fw(adp5585, kpad); + if (error) + return error; + + error = adp5585_keys_setup(adp5585, kpad); + if (error) + return error; + + kpad->nb.notifier_call = adp5585_keys_ev_handle; + error = blocking_notifier_chain_register(&adp5585->event_notifier, + &kpad->nb); + if (error) + return error; + + error = devm_add_action_or_reset(dev, adp5585_keys_unreg_notifier, kpad); + if (error) + return error; + + error = input_register_device(kpad->input); + if (error) + return dev_err_probe(dev, error, + "Failed to register input device\n"); + + return 0; +} + +static const struct adp5585_kpad_chip adp5585_kpad_chip_info = { + .max_rows = 6, + .max_cols = 5, + .key_ev_min = ADP5585_ROW5_KEY_EVENT_START, + .key_ev_max = ADP5585_ROW5_KEY_EVENT_END, +}; + +static const struct adp5585_kpad_chip adp5589_kpad_chip_info = { + .max_rows = 8, + .max_cols = 11, + .key_ev_min = ADP5589_KEY_EVENT_START, + .key_ev_max = ADP5589_KEY_EVENT_END, +}; + +static const struct platform_device_id adp5585_keys_id_table[] = { + { "adp5585-keys", (kernel_ulong_t)&adp5585_kpad_chip_info }, + { "adp5589-keys", (kernel_ulong_t)&adp5589_kpad_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(platform, adp5585_keys_id_table); + +static struct platform_driver adp5585_keys_driver = { + .driver = { + .name = "adp5585-keys", + }, + .probe = adp5585_keys_probe, + .id_table = adp5585_keys_id_table, +}; +module_platform_driver(adp5585_keys_driver); + +MODULE_AUTHOR("Nuno Sá "); +MODULE_DESCRIPTION("ADP5585 Keys Driver"); +MODULE_LICENSE("GPL"); -- GitLab From 3bdbd0858df6574b71cacaac073f117d65a36dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:12 +0100 Subject: [PATCH 347/868] Input: adp5589: remove the driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adp5589 support is based on legacy platform data and there's no upstream platform using this device. Moreover, recently, with commit 480a8ad683d7 ("mfd: adp5585: Add Analog Devices ADP5585 core support") we overlapped support for the adp5585 device (gpiochip part of it) but since it actually makes sense for the device to be supported under MFD, we can complement it and add the keymap support for it (properly based on FW properties). And that is what commit 04840c5363a6 ("Input: adp5585: Add Analog Devices ADP5585/89 support") is doing. Reviewed-by: Laurent Pinchart Acked-by: Dmitry Torokhov Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-17-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/input/keyboard/Kconfig | 10 - drivers/input/keyboard/Makefile | 1 - drivers/input/keyboard/adp5589-keys.c | 1066 ------------------------- 3 files changed, 1077 deletions(-) delete mode 100644 drivers/input/keyboard/adp5589-keys.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index e1f8cf2ac305d..7c4f309a4cb63 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -61,16 +61,6 @@ config KEYBOARD_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-keys. -config KEYBOARD_ADP5589 - tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander" - depends on I2C - help - Say Y here if you want to use a ADP5585/ADP5589 attached to your - system I2C bus. - - To compile this driver as a module, choose M here: the - module will be called adp5589-keys. - config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index f00ec003a59aa..8bc20ab2b103b 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o -obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c deleted file mode 100644 index 81d0876ee358e..0000000000000 --- a/drivers/input/keyboard/adp5589-keys.c +++ /dev/null @@ -1,1066 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Description: keypad driver for ADP5589, ADP5585 - * I2C QWERTY Keypad and IO Expander - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * Copyright (C) 2010-2011 Analog Devices Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* ADP5589/ADP5585 Common Registers */ -#define ADP5589_5_ID 0x00 -#define ADP5589_5_INT_STATUS 0x01 -#define ADP5589_5_STATUS 0x02 -#define ADP5589_5_FIFO_1 0x03 -#define ADP5589_5_FIFO_2 0x04 -#define ADP5589_5_FIFO_3 0x05 -#define ADP5589_5_FIFO_4 0x06 -#define ADP5589_5_FIFO_5 0x07 -#define ADP5589_5_FIFO_6 0x08 -#define ADP5589_5_FIFO_7 0x09 -#define ADP5589_5_FIFO_8 0x0A -#define ADP5589_5_FIFO_9 0x0B -#define ADP5589_5_FIFO_10 0x0C -#define ADP5589_5_FIFO_11 0x0D -#define ADP5589_5_FIFO_12 0x0E -#define ADP5589_5_FIFO_13 0x0F -#define ADP5589_5_FIFO_14 0x10 -#define ADP5589_5_FIFO_15 0x11 -#define ADP5589_5_FIFO_16 0x12 -#define ADP5589_5_GPI_INT_STAT_A 0x13 -#define ADP5589_5_GPI_INT_STAT_B 0x14 - -/* ADP5589 Registers */ -#define ADP5589_GPI_INT_STAT_C 0x15 -#define ADP5589_GPI_STATUS_A 0x16 -#define ADP5589_GPI_STATUS_B 0x17 -#define ADP5589_GPI_STATUS_C 0x18 -#define ADP5589_RPULL_CONFIG_A 0x19 -#define ADP5589_RPULL_CONFIG_B 0x1A -#define ADP5589_RPULL_CONFIG_C 0x1B -#define ADP5589_RPULL_CONFIG_D 0x1C -#define ADP5589_RPULL_CONFIG_E 0x1D -#define ADP5589_GPI_INT_LEVEL_A 0x1E -#define ADP5589_GPI_INT_LEVEL_B 0x1F -#define ADP5589_GPI_INT_LEVEL_C 0x20 -#define ADP5589_GPI_EVENT_EN_A 0x21 -#define ADP5589_GPI_EVENT_EN_B 0x22 -#define ADP5589_GPI_EVENT_EN_C 0x23 -#define ADP5589_GPI_INTERRUPT_EN_A 0x24 -#define ADP5589_GPI_INTERRUPT_EN_B 0x25 -#define ADP5589_GPI_INTERRUPT_EN_C 0x26 -#define ADP5589_DEBOUNCE_DIS_A 0x27 -#define ADP5589_DEBOUNCE_DIS_B 0x28 -#define ADP5589_DEBOUNCE_DIS_C 0x29 -#define ADP5589_GPO_DATA_OUT_A 0x2A -#define ADP5589_GPO_DATA_OUT_B 0x2B -#define ADP5589_GPO_DATA_OUT_C 0x2C -#define ADP5589_GPO_OUT_MODE_A 0x2D -#define ADP5589_GPO_OUT_MODE_B 0x2E -#define ADP5589_GPO_OUT_MODE_C 0x2F -#define ADP5589_GPIO_DIRECTION_A 0x30 -#define ADP5589_GPIO_DIRECTION_B 0x31 -#define ADP5589_GPIO_DIRECTION_C 0x32 -#define ADP5589_UNLOCK1 0x33 -#define ADP5589_UNLOCK2 0x34 -#define ADP5589_EXT_LOCK_EVENT 0x35 -#define ADP5589_UNLOCK_TIMERS 0x36 -#define ADP5589_LOCK_CFG 0x37 -#define ADP5589_RESET1_EVENT_A 0x38 -#define ADP5589_RESET1_EVENT_B 0x39 -#define ADP5589_RESET1_EVENT_C 0x3A -#define ADP5589_RESET2_EVENT_A 0x3B -#define ADP5589_RESET2_EVENT_B 0x3C -#define ADP5589_RESET_CFG 0x3D -#define ADP5589_PWM_OFFT_LOW 0x3E -#define ADP5589_PWM_OFFT_HIGH 0x3F -#define ADP5589_PWM_ONT_LOW 0x40 -#define ADP5589_PWM_ONT_HIGH 0x41 -#define ADP5589_PWM_CFG 0x42 -#define ADP5589_CLOCK_DIV_CFG 0x43 -#define ADP5589_LOGIC_1_CFG 0x44 -#define ADP5589_LOGIC_2_CFG 0x45 -#define ADP5589_LOGIC_FF_CFG 0x46 -#define ADP5589_LOGIC_INT_EVENT_EN 0x47 -#define ADP5589_POLL_PTIME_CFG 0x48 -#define ADP5589_PIN_CONFIG_A 0x49 -#define ADP5589_PIN_CONFIG_B 0x4A -#define ADP5589_PIN_CONFIG_C 0x4B -#define ADP5589_PIN_CONFIG_D 0x4C -#define ADP5589_GENERAL_CFG 0x4D -#define ADP5589_INT_EN 0x4E - -/* ADP5585 Registers */ -#define ADP5585_GPI_STATUS_A 0x15 -#define ADP5585_GPI_STATUS_B 0x16 -#define ADP5585_RPULL_CONFIG_A 0x17 -#define ADP5585_RPULL_CONFIG_B 0x18 -#define ADP5585_RPULL_CONFIG_C 0x19 -#define ADP5585_RPULL_CONFIG_D 0x1A -#define ADP5585_GPI_INT_LEVEL_A 0x1B -#define ADP5585_GPI_INT_LEVEL_B 0x1C -#define ADP5585_GPI_EVENT_EN_A 0x1D -#define ADP5585_GPI_EVENT_EN_B 0x1E -#define ADP5585_GPI_INTERRUPT_EN_A 0x1F -#define ADP5585_GPI_INTERRUPT_EN_B 0x20 -#define ADP5585_DEBOUNCE_DIS_A 0x21 -#define ADP5585_DEBOUNCE_DIS_B 0x22 -#define ADP5585_GPO_DATA_OUT_A 0x23 -#define ADP5585_GPO_DATA_OUT_B 0x24 -#define ADP5585_GPO_OUT_MODE_A 0x25 -#define ADP5585_GPO_OUT_MODE_B 0x26 -#define ADP5585_GPIO_DIRECTION_A 0x27 -#define ADP5585_GPIO_DIRECTION_B 0x28 -#define ADP5585_RESET1_EVENT_A 0x29 -#define ADP5585_RESET1_EVENT_B 0x2A -#define ADP5585_RESET1_EVENT_C 0x2B -#define ADP5585_RESET2_EVENT_A 0x2C -#define ADP5585_RESET2_EVENT_B 0x2D -#define ADP5585_RESET_CFG 0x2E -#define ADP5585_PWM_OFFT_LOW 0x2F -#define ADP5585_PWM_OFFT_HIGH 0x30 -#define ADP5585_PWM_ONT_LOW 0x31 -#define ADP5585_PWM_ONT_HIGH 0x32 -#define ADP5585_PWM_CFG 0x33 -#define ADP5585_LOGIC_CFG 0x34 -#define ADP5585_LOGIC_FF_CFG 0x35 -#define ADP5585_LOGIC_INT_EVENT_EN 0x36 -#define ADP5585_POLL_PTIME_CFG 0x37 -#define ADP5585_PIN_CONFIG_A 0x38 -#define ADP5585_PIN_CONFIG_B 0x39 -#define ADP5585_PIN_CONFIG_D 0x3A -#define ADP5585_GENERAL_CFG 0x3B -#define ADP5585_INT_EN 0x3C - -/* ID Register */ -#define ADP5589_5_DEVICE_ID_MASK 0xF -#define ADP5589_5_MAN_ID_MASK 0xF -#define ADP5589_5_MAN_ID_SHIFT 4 -#define ADP5589_5_MAN_ID 0x02 - -/* GENERAL_CFG Register */ -#define OSC_EN BIT(7) -#define CORE_CLK(x) (((x) & 0x3) << 5) -#define LCK_TRK_LOGIC BIT(4) /* ADP5589 only */ -#define LCK_TRK_GPI BIT(3) /* ADP5589 only */ -#define INT_CFG BIT(1) -#define RST_CFG BIT(0) - -/* INT_EN Register */ -#define LOGIC2_IEN BIT(5) /* ADP5589 only */ -#define LOGIC1_IEN BIT(4) -#define LOCK_IEN BIT(3) /* ADP5589 only */ -#define OVRFLOW_IEN BIT(2) -#define GPI_IEN BIT(1) -#define EVENT_IEN BIT(0) - -/* Interrupt Status Register */ -#define LOGIC2_INT BIT(5) /* ADP5589 only */ -#define LOGIC1_INT BIT(4) -#define LOCK_INT BIT(3) /* ADP5589 only */ -#define OVRFLOW_INT BIT(2) -#define GPI_INT BIT(1) -#define EVENT_INT BIT(0) - -/* STATUS Register */ -#define LOGIC2_STAT BIT(7) /* ADP5589 only */ -#define LOGIC1_STAT BIT(6) -#define LOCK_STAT BIT(5) /* ADP5589 only */ -#define KEC 0x1F - -/* PIN_CONFIG_D Register */ -#define C4_EXTEND_CFG BIT(6) /* RESET2 */ -#define R4_EXTEND_CFG BIT(5) /* RESET1 */ - -/* LOCK_CFG */ -#define LOCK_EN BIT(0) - -#define PTIME_MASK 0x3 -#define LTIME_MASK 0x3 /* ADP5589 only */ - -/* Key Event Register xy */ -#define KEY_EV_PRESSED BIT(7) -#define KEY_EV_MASK 0x7F - -#define KEYP_MAX_EVENT 16 -#define ADP5589_MAXGPIO 19 -#define ADP5585_MAXGPIO 11 /* 10 on the ADP5585-01, 11 on ADP5585-02 */ - -enum { - ADP5589, - ADP5585_01, - ADP5585_02 -}; - -struct adp_constants { - u8 maxgpio; - u8 keymapsize; - u8 gpi_pin_row_base; - u8 gpi_pin_row_end; - u8 gpi_pin_col_base; - u8 gpi_pin_base; - u8 gpi_pin_end; - u8 gpimapsize_max; - u8 max_row_num; - u8 max_col_num; - u8 row_mask; - u8 col_mask; - u8 col_shift; - u8 c4_extend_cfg; - u8 (*bank) (u8 offset); - u8 (*bit) (u8 offset); - u8 (*reg) (u8 reg); -}; - -struct adp5589_kpad { - struct i2c_client *client; - struct input_dev *input; - const struct adp_constants *var; - unsigned short keycode[ADP5589_KEYMAPSIZE]; - const struct adp5589_gpi_map *gpimap; - unsigned short gpimapsize; - unsigned extend_cfg; - bool is_adp5585; - bool support_row5; -#ifdef CONFIG_GPIOLIB - unsigned char gpiomap[ADP5589_MAXGPIO]; - struct gpio_chip gc; - struct mutex gpio_lock; /* Protect cached dir, dat_out */ - u8 dat_out[3]; - u8 dir[3]; -#endif -}; - -/* - * ADP5589 / ADP5585 derivative / variant handling - */ - - -/* ADP5589 */ - -static unsigned char adp5589_bank(unsigned char offset) -{ - return offset >> 3; -} - -static unsigned char adp5589_bit(unsigned char offset) -{ - return 1u << (offset & 0x7); -} - -static unsigned char adp5589_reg(unsigned char reg) -{ - return reg; -} - -static const struct adp_constants const_adp5589 = { - .maxgpio = ADP5589_MAXGPIO, - .keymapsize = ADP5589_KEYMAPSIZE, - .gpi_pin_row_base = ADP5589_GPI_PIN_ROW_BASE, - .gpi_pin_row_end = ADP5589_GPI_PIN_ROW_END, - .gpi_pin_col_base = ADP5589_GPI_PIN_COL_BASE, - .gpi_pin_base = ADP5589_GPI_PIN_BASE, - .gpi_pin_end = ADP5589_GPI_PIN_END, - .gpimapsize_max = ADP5589_GPIMAPSIZE_MAX, - .c4_extend_cfg = 12, - .max_row_num = ADP5589_MAX_ROW_NUM, - .max_col_num = ADP5589_MAX_COL_NUM, - .row_mask = ADP5589_ROW_MASK, - .col_mask = ADP5589_COL_MASK, - .col_shift = ADP5589_COL_SHIFT, - .bank = adp5589_bank, - .bit = adp5589_bit, - .reg = adp5589_reg, -}; - -/* ADP5585 */ - -static unsigned char adp5585_bank(unsigned char offset) -{ - return offset > ADP5585_MAX_ROW_NUM; -} - -static unsigned char adp5585_bit(unsigned char offset) -{ - return (offset > ADP5585_MAX_ROW_NUM) ? - 1u << (offset - ADP5585_COL_SHIFT) : 1u << offset; -} - -static const unsigned char adp5585_reg_lut[] = { - [ADP5589_GPI_STATUS_A] = ADP5585_GPI_STATUS_A, - [ADP5589_GPI_STATUS_B] = ADP5585_GPI_STATUS_B, - [ADP5589_RPULL_CONFIG_A] = ADP5585_RPULL_CONFIG_A, - [ADP5589_RPULL_CONFIG_B] = ADP5585_RPULL_CONFIG_B, - [ADP5589_RPULL_CONFIG_C] = ADP5585_RPULL_CONFIG_C, - [ADP5589_RPULL_CONFIG_D] = ADP5585_RPULL_CONFIG_D, - [ADP5589_GPI_INT_LEVEL_A] = ADP5585_GPI_INT_LEVEL_A, - [ADP5589_GPI_INT_LEVEL_B] = ADP5585_GPI_INT_LEVEL_B, - [ADP5589_GPI_EVENT_EN_A] = ADP5585_GPI_EVENT_EN_A, - [ADP5589_GPI_EVENT_EN_B] = ADP5585_GPI_EVENT_EN_B, - [ADP5589_GPI_INTERRUPT_EN_A] = ADP5585_GPI_INTERRUPT_EN_A, - [ADP5589_GPI_INTERRUPT_EN_B] = ADP5585_GPI_INTERRUPT_EN_B, - [ADP5589_DEBOUNCE_DIS_A] = ADP5585_DEBOUNCE_DIS_A, - [ADP5589_DEBOUNCE_DIS_B] = ADP5585_DEBOUNCE_DIS_B, - [ADP5589_GPO_DATA_OUT_A] = ADP5585_GPO_DATA_OUT_A, - [ADP5589_GPO_DATA_OUT_B] = ADP5585_GPO_DATA_OUT_B, - [ADP5589_GPO_OUT_MODE_A] = ADP5585_GPO_OUT_MODE_A, - [ADP5589_GPO_OUT_MODE_B] = ADP5585_GPO_OUT_MODE_B, - [ADP5589_GPIO_DIRECTION_A] = ADP5585_GPIO_DIRECTION_A, - [ADP5589_GPIO_DIRECTION_B] = ADP5585_GPIO_DIRECTION_B, - [ADP5589_RESET1_EVENT_A] = ADP5585_RESET1_EVENT_A, - [ADP5589_RESET1_EVENT_B] = ADP5585_RESET1_EVENT_B, - [ADP5589_RESET1_EVENT_C] = ADP5585_RESET1_EVENT_C, - [ADP5589_RESET2_EVENT_A] = ADP5585_RESET2_EVENT_A, - [ADP5589_RESET2_EVENT_B] = ADP5585_RESET2_EVENT_B, - [ADP5589_RESET_CFG] = ADP5585_RESET_CFG, - [ADP5589_PWM_OFFT_LOW] = ADP5585_PWM_OFFT_LOW, - [ADP5589_PWM_OFFT_HIGH] = ADP5585_PWM_OFFT_HIGH, - [ADP5589_PWM_ONT_LOW] = ADP5585_PWM_ONT_LOW, - [ADP5589_PWM_ONT_HIGH] = ADP5585_PWM_ONT_HIGH, - [ADP5589_PWM_CFG] = ADP5585_PWM_CFG, - [ADP5589_LOGIC_1_CFG] = ADP5585_LOGIC_CFG, - [ADP5589_LOGIC_FF_CFG] = ADP5585_LOGIC_FF_CFG, - [ADP5589_LOGIC_INT_EVENT_EN] = ADP5585_LOGIC_INT_EVENT_EN, - [ADP5589_POLL_PTIME_CFG] = ADP5585_POLL_PTIME_CFG, - [ADP5589_PIN_CONFIG_A] = ADP5585_PIN_CONFIG_A, - [ADP5589_PIN_CONFIG_B] = ADP5585_PIN_CONFIG_B, - [ADP5589_PIN_CONFIG_D] = ADP5585_PIN_CONFIG_D, - [ADP5589_GENERAL_CFG] = ADP5585_GENERAL_CFG, - [ADP5589_INT_EN] = ADP5585_INT_EN, -}; - -static unsigned char adp5585_reg(unsigned char reg) -{ - return adp5585_reg_lut[reg]; -} - -static const struct adp_constants const_adp5585 = { - .maxgpio = ADP5585_MAXGPIO, - .keymapsize = ADP5585_KEYMAPSIZE, - .gpi_pin_row_base = ADP5585_GPI_PIN_ROW_BASE, - .gpi_pin_row_end = ADP5585_GPI_PIN_ROW_END, - .gpi_pin_col_base = ADP5585_GPI_PIN_COL_BASE, - .gpi_pin_base = ADP5585_GPI_PIN_BASE, - .gpi_pin_end = ADP5585_GPI_PIN_END, - .gpimapsize_max = ADP5585_GPIMAPSIZE_MAX, - .c4_extend_cfg = 10, - .max_row_num = ADP5585_MAX_ROW_NUM, - .max_col_num = ADP5585_MAX_COL_NUM, - .row_mask = ADP5585_ROW_MASK, - .col_mask = ADP5585_COL_MASK, - .col_shift = ADP5585_COL_SHIFT, - .bank = adp5585_bank, - .bit = adp5585_bit, - .reg = adp5585_reg, -}; - -static int adp5589_read(struct i2c_client *client, u8 reg) -{ - int ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "Read Error\n"); - - return ret; -} - -static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) -{ - return i2c_smbus_write_byte_data(client, reg, val); -} - -#ifdef CONFIG_GPIOLIB -static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) -{ - struct adp5589_kpad *kpad = gpiochip_get_data(chip); - unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); - unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - int val; - - mutex_lock(&kpad->gpio_lock); - if (kpad->dir[bank] & bit) - val = kpad->dat_out[bank]; - else - val = adp5589_read(kpad->client, - kpad->var->reg(ADP5589_GPI_STATUS_A) + bank); - mutex_unlock(&kpad->gpio_lock); - - return !!(val & bit); -} - -static void adp5589_gpio_set_value(struct gpio_chip *chip, - unsigned off, int val) -{ - struct adp5589_kpad *kpad = gpiochip_get_data(chip); - unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); - unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - - guard(mutex)(&kpad->gpio_lock); - - if (val) - kpad->dat_out[bank] |= bit; - else - kpad->dat_out[bank] &= ~bit; - - adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + - bank, kpad->dat_out[bank]); -} - -static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) -{ - struct adp5589_kpad *kpad = gpiochip_get_data(chip); - unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); - unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - - guard(mutex)(&kpad->gpio_lock); - - kpad->dir[bank] &= ~bit; - return adp5589_write(kpad->client, - kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, - kpad->dir[bank]); -} - -static int adp5589_gpio_direction_output(struct gpio_chip *chip, - unsigned off, int val) -{ - struct adp5589_kpad *kpad = gpiochip_get_data(chip); - unsigned int bank = kpad->var->bank(kpad->gpiomap[off]); - unsigned int bit = kpad->var->bit(kpad->gpiomap[off]); - int error; - - guard(mutex)(&kpad->gpio_lock); - - kpad->dir[bank] |= bit; - - if (val) - kpad->dat_out[bank] |= bit; - else - kpad->dat_out[bank] &= ~bit; - - error = adp5589_write(kpad->client, - kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank, - kpad->dat_out[bank]); - if (error) - return error; - - error = adp5589_write(kpad->client, - kpad->var->reg(ADP5589_GPIO_DIRECTION_A) + bank, - kpad->dir[bank]); - if (error) - return error; - - return 0; -} - -static int adp5589_build_gpiomap(struct adp5589_kpad *kpad, - const struct adp5589_kpad_platform_data *pdata) -{ - bool pin_used[ADP5589_MAXGPIO]; - int n_unused = 0; - int i; - - memset(pin_used, false, sizeof(pin_used)); - - for (i = 0; i < kpad->var->maxgpio; i++) - if (pdata->keypad_en_mask & BIT(i)) - pin_used[i] = true; - - for (i = 0; i < kpad->gpimapsize; i++) - pin_used[kpad->gpimap[i].pin - kpad->var->gpi_pin_base] = true; - - if (kpad->extend_cfg & R4_EXTEND_CFG) - pin_used[4] = true; - - if (kpad->extend_cfg & C4_EXTEND_CFG) - pin_used[kpad->var->c4_extend_cfg] = true; - - if (!kpad->support_row5) - pin_used[5] = true; - - for (i = 0; i < kpad->var->maxgpio; i++) - if (!pin_used[i]) - kpad->gpiomap[n_unused++] = i; - - return n_unused; -} - -static int adp5589_gpio_add(struct adp5589_kpad *kpad) -{ - struct device *dev = &kpad->client->dev; - const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(dev); - const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; - int i, error; - - if (!gpio_data) - return 0; - - kpad->gc.parent = dev; - kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); - if (kpad->gc.ngpio == 0) { - dev_info(dev, "No unused gpios left to export\n"); - return 0; - } - - kpad->gc.direction_input = adp5589_gpio_direction_input; - kpad->gc.direction_output = adp5589_gpio_direction_output; - kpad->gc.get = adp5589_gpio_get_value; - kpad->gc.set = adp5589_gpio_set_value; - kpad->gc.can_sleep = 1; - - kpad->gc.base = gpio_data->gpio_start; - kpad->gc.label = kpad->client->name; - kpad->gc.owner = THIS_MODULE; - - mutex_init(&kpad->gpio_lock); - - error = devm_gpiochip_add_data(dev, &kpad->gc, kpad); - if (error) - return error; - - for (i = 0; i <= kpad->var->bank(kpad->var->maxgpio); i++) { - kpad->dat_out[i] = adp5589_read(kpad->client, kpad->var->reg( - ADP5589_GPO_DATA_OUT_A) + i); - kpad->dir[i] = adp5589_read(kpad->client, kpad->var->reg( - ADP5589_GPIO_DIRECTION_A) + i); - } - - return 0; -} -#else -static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) -{ - return 0; -} -#endif - -static void adp5589_report_switches(struct adp5589_kpad *kpad, - int key, int key_val) -{ - int i; - - for (i = 0; i < kpad->gpimapsize; i++) { - if (key_val == kpad->gpimap[i].pin) { - input_report_switch(kpad->input, - kpad->gpimap[i].sw_evt, - key & KEY_EV_PRESSED); - break; - } - } -} - -static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) -{ - int i; - - for (i = 0; i < ev_cnt; i++) { - int key = adp5589_read(kpad->client, ADP5589_5_FIFO_1 + i); - int key_val = key & KEY_EV_MASK; - - if (key_val >= kpad->var->gpi_pin_base && - key_val <= kpad->var->gpi_pin_end) { - adp5589_report_switches(kpad, key, key_val); - } else { - input_report_key(kpad->input, - kpad->keycode[key_val - 1], - key & KEY_EV_PRESSED); - } - } -} - -static irqreturn_t adp5589_irq(int irq, void *handle) -{ - struct adp5589_kpad *kpad = handle; - struct i2c_client *client = kpad->client; - int status, ev_cnt; - - status = adp5589_read(client, ADP5589_5_INT_STATUS); - - if (status & OVRFLOW_INT) /* Unlikely and should never happen */ - dev_err(&client->dev, "Event Overflow Error\n"); - - if (status & EVENT_INT) { - ev_cnt = adp5589_read(client, ADP5589_5_STATUS) & KEC; - if (ev_cnt) { - adp5589_report_events(kpad, ev_cnt); - input_sync(kpad->input); - } - } - - adp5589_write(client, ADP5589_5_INT_STATUS, status); /* Status is W1C */ - - return IRQ_HANDLED; -} - -static int adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key) -{ - int i; - - for (i = 0; i < kpad->var->keymapsize; i++) - if (key == kpad->keycode[i]) - return (i + 1) | KEY_EV_PRESSED; - - dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); - - return -EINVAL; -} - -static int adp5589_setup(struct adp5589_kpad *kpad) -{ - struct i2c_client *client = kpad->client; - const struct adp5589_kpad_platform_data *pdata = - dev_get_platdata(&client->dev); - u8 (*reg) (u8) = kpad->var->reg; - unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; - unsigned char pull_mask = 0; - int i, ret; - - ret = adp5589_write(client, reg(ADP5589_PIN_CONFIG_A), - pdata->keypad_en_mask & kpad->var->row_mask); - ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_B), - (pdata->keypad_en_mask >> kpad->var->col_shift) & - kpad->var->col_mask); - - if (!kpad->is_adp5585) - ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, - (pdata->keypad_en_mask >> 16) & 0xFF); - - if (!kpad->is_adp5585 && pdata->en_keylock) { - ret |= adp5589_write(client, ADP5589_UNLOCK1, - pdata->unlock_key1); - ret |= adp5589_write(client, ADP5589_UNLOCK2, - pdata->unlock_key2); - ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, - pdata->unlock_timer & LTIME_MASK); - ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); - } - - for (i = 0; i < KEYP_MAX_EVENT; i++) - ret |= adp5589_read(client, ADP5589_5_FIFO_1 + i); - - for (i = 0; i < pdata->gpimapsize; i++) { - unsigned short pin = pdata->gpimap[i].pin; - - if (pin <= kpad->var->gpi_pin_row_end) { - evt_mode1 |= BIT(pin - kpad->var->gpi_pin_row_base); - } else { - evt_mode2 |= - BIT(pin - kpad->var->gpi_pin_col_base) & 0xFF; - if (!kpad->is_adp5585) - evt_mode3 |= - BIT(pin - kpad->var->gpi_pin_col_base) >> 8; - } - } - - if (pdata->gpimapsize) { - ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_A), - evt_mode1); - ret |= adp5589_write(client, reg(ADP5589_GPI_EVENT_EN_B), - evt_mode2); - if (!kpad->is_adp5585) - ret |= adp5589_write(client, - reg(ADP5589_GPI_EVENT_EN_C), - evt_mode3); - } - - if (pdata->pull_dis_mask & pdata->pullup_en_100k & - pdata->pullup_en_300k & pdata->pulldown_en_300k) - dev_warn(&client->dev, "Conflicting pull resistor config\n"); - - for (i = 0; i <= kpad->var->max_row_num; i++) { - unsigned int val = 0, bit = BIT(i); - if (pdata->pullup_en_300k & bit) - val = 0; - else if (pdata->pulldown_en_300k & bit) - val = 1; - else if (pdata->pullup_en_100k & bit) - val = 2; - else if (pdata->pull_dis_mask & bit) - val = 3; - - pull_mask |= val << (2 * (i & 0x3)); - - if (i % 4 == 3 || i == kpad->var->max_row_num) { - ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) - + (i >> 2), pull_mask); - pull_mask = 0; - } - } - - for (i = 0; i <= kpad->var->max_col_num; i++) { - unsigned int val = 0, bit = BIT(i + kpad->var->col_shift); - if (pdata->pullup_en_300k & bit) - val = 0; - else if (pdata->pulldown_en_300k & bit) - val = 1; - else if (pdata->pullup_en_100k & bit) - val = 2; - else if (pdata->pull_dis_mask & bit) - val = 3; - - pull_mask |= val << (2 * (i & 0x3)); - - if (i % 4 == 3 || i == kpad->var->max_col_num) { - ret |= adp5589_write(client, - reg(ADP5585_RPULL_CONFIG_C) + - (i >> 2), pull_mask); - pull_mask = 0; - } - } - - if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { - ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_A), - adp5589_get_evcode(kpad, - pdata->reset1_key_1)); - ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_B), - adp5589_get_evcode(kpad, - pdata->reset1_key_2)); - ret |= adp5589_write(client, reg(ADP5589_RESET1_EVENT_C), - adp5589_get_evcode(kpad, - pdata->reset1_key_3)); - kpad->extend_cfg |= R4_EXTEND_CFG; - } - - if (pdata->reset2_key_1 && pdata->reset2_key_2) { - ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_A), - adp5589_get_evcode(kpad, - pdata->reset2_key_1)); - ret |= adp5589_write(client, reg(ADP5589_RESET2_EVENT_B), - adp5589_get_evcode(kpad, - pdata->reset2_key_2)); - kpad->extend_cfg |= C4_EXTEND_CFG; - } - - if (kpad->extend_cfg) { - ret |= adp5589_write(client, reg(ADP5589_RESET_CFG), - pdata->reset_cfg); - ret |= adp5589_write(client, reg(ADP5589_PIN_CONFIG_D), - kpad->extend_cfg); - } - - ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_A), - pdata->debounce_dis_mask & kpad->var->row_mask); - - ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_B), - (pdata->debounce_dis_mask >> kpad->var->col_shift) - & kpad->var->col_mask); - - if (!kpad->is_adp5585) - ret |= adp5589_write(client, reg(ADP5589_DEBOUNCE_DIS_C), - (pdata->debounce_dis_mask >> 16) & 0xFF); - - ret |= adp5589_write(client, reg(ADP5589_POLL_PTIME_CFG), - pdata->scan_cycle_time & PTIME_MASK); - ret |= adp5589_write(client, ADP5589_5_INT_STATUS, - (kpad->is_adp5585 ? 0 : LOGIC2_INT) | - LOGIC1_INT | OVRFLOW_INT | - (kpad->is_adp5585 ? 0 : LOCK_INT) | - GPI_INT | EVENT_INT); /* Status is W1C */ - - ret |= adp5589_write(client, reg(ADP5589_GENERAL_CFG), - INT_CFG | OSC_EN | CORE_CLK(3)); - ret |= adp5589_write(client, reg(ADP5589_INT_EN), - OVRFLOW_IEN | GPI_IEN | EVENT_IEN); - - if (ret < 0) { - dev_err(&client->dev, "Write Error\n"); - return ret; - } - - return 0; -} - -static void adp5589_report_switch_state(struct adp5589_kpad *kpad) -{ - int gpi_stat_tmp, pin_loc; - int i; - int gpi_stat1 = adp5589_read(kpad->client, - kpad->var->reg(ADP5589_GPI_STATUS_A)); - int gpi_stat2 = adp5589_read(kpad->client, - kpad->var->reg(ADP5589_GPI_STATUS_B)); - int gpi_stat3 = !kpad->is_adp5585 ? - adp5589_read(kpad->client, ADP5589_GPI_STATUS_C) : 0; - - for (i = 0; i < kpad->gpimapsize; i++) { - unsigned short pin = kpad->gpimap[i].pin; - - if (pin <= kpad->var->gpi_pin_row_end) { - gpi_stat_tmp = gpi_stat1; - pin_loc = pin - kpad->var->gpi_pin_row_base; - } else if ((pin - kpad->var->gpi_pin_col_base) < 8) { - gpi_stat_tmp = gpi_stat2; - pin_loc = pin - kpad->var->gpi_pin_col_base; - } else { - gpi_stat_tmp = gpi_stat3; - pin_loc = pin - kpad->var->gpi_pin_col_base - 8; - } - - if (gpi_stat_tmp < 0) { - dev_err(&kpad->client->dev, - "Can't read GPIO_DAT_STAT switch %d, default to OFF\n", - pin); - gpi_stat_tmp = 0; - } - - input_report_switch(kpad->input, - kpad->gpimap[i].sw_evt, - !(gpi_stat_tmp & BIT(pin_loc))); - } - - input_sync(kpad->input); -} - -static int adp5589_keypad_add(struct adp5589_kpad *kpad, unsigned int revid) -{ - struct i2c_client *client = kpad->client; - const struct adp5589_kpad_platform_data *pdata = - dev_get_platdata(&client->dev); - struct input_dev *input; - unsigned int i; - int error; - - if (!((pdata->keypad_en_mask & kpad->var->row_mask) && - (pdata->keypad_en_mask >> kpad->var->col_shift)) || - !pdata->keymap) { - dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); - return -EINVAL; - } - - if (pdata->keymapsize != kpad->var->keymapsize) { - dev_err(&client->dev, "invalid keymapsize\n"); - return -EINVAL; - } - - if (!pdata->gpimap && pdata->gpimapsize) { - dev_err(&client->dev, "invalid gpimap from pdata\n"); - return -EINVAL; - } - - if (pdata->gpimapsize > kpad->var->gpimapsize_max) { - dev_err(&client->dev, "invalid gpimapsize\n"); - return -EINVAL; - } - - for (i = 0; i < pdata->gpimapsize; i++) { - unsigned short pin = pdata->gpimap[i].pin; - - if (pin < kpad->var->gpi_pin_base || - pin > kpad->var->gpi_pin_end) { - dev_err(&client->dev, "invalid gpi pin data\n"); - return -EINVAL; - } - - if (BIT(pin - kpad->var->gpi_pin_row_base) & - pdata->keypad_en_mask) { - dev_err(&client->dev, "invalid gpi row/col data\n"); - return -EINVAL; - } - } - - if (!client->irq) { - dev_err(&client->dev, "no IRQ?\n"); - return -EINVAL; - } - - input = devm_input_allocate_device(&client->dev); - if (!input) - return -ENOMEM; - - kpad->input = input; - - input->name = client->name; - input->phys = "adp5589-keys/input0"; - input->dev.parent = &client->dev; - - input_set_drvdata(input, kpad); - - input->id.bustype = BUS_I2C; - input->id.vendor = 0x0001; - input->id.product = 0x0001; - input->id.version = revid; - - input->keycodesize = sizeof(kpad->keycode[0]); - input->keycodemax = pdata->keymapsize; - input->keycode = kpad->keycode; - - memcpy(kpad->keycode, pdata->keymap, - pdata->keymapsize * input->keycodesize); - - kpad->gpimap = pdata->gpimap; - kpad->gpimapsize = pdata->gpimapsize; - - /* setup input device */ - __set_bit(EV_KEY, input->evbit); - - if (pdata->repeat) - __set_bit(EV_REP, input->evbit); - - for (i = 0; i < input->keycodemax; i++) - if (kpad->keycode[i] <= KEY_MAX) - __set_bit(kpad->keycode[i], input->keybit); - __clear_bit(KEY_RESERVED, input->keybit); - - if (kpad->gpimapsize) - __set_bit(EV_SW, input->evbit); - for (i = 0; i < kpad->gpimapsize; i++) - __set_bit(kpad->gpimap[i].sw_evt, input->swbit); - - error = input_register_device(input); - if (error) { - dev_err(&client->dev, "unable to register input device\n"); - return error; - } - - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, adp5589_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->dev.driver->name, kpad); - if (error) { - dev_err(&client->dev, "unable to request irq %d\n", client->irq); - return error; - } - - return 0; -} - -static void adp5589_clear_config(void *data) -{ - struct adp5589_kpad *kpad = data; - - adp5589_write(kpad->client, kpad->var->reg(ADP5589_GENERAL_CFG), 0); -} - -static int adp5589_probe(struct i2c_client *client) -{ - const struct i2c_device_id *id = i2c_client_get_device_id(client); - struct adp5589_kpad *kpad; - const struct adp5589_kpad_platform_data *pdata = - dev_get_platdata(&client->dev); - unsigned int revid; - int error, ret; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); - return -EIO; - } - - if (!pdata) { - dev_err(&client->dev, "no platform data?\n"); - return -EINVAL; - } - - kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL); - if (!kpad) - return -ENOMEM; - - kpad->client = client; - - switch (id->driver_data) { - case ADP5585_02: - kpad->support_row5 = true; - fallthrough; - case ADP5585_01: - kpad->is_adp5585 = true; - kpad->var = &const_adp5585; - break; - case ADP5589: - kpad->support_row5 = true; - kpad->var = &const_adp5589; - break; - } - - error = devm_add_action_or_reset(&client->dev, adp5589_clear_config, - kpad); - if (error) - return error; - - ret = adp5589_read(client, ADP5589_5_ID); - if (ret < 0) - return ret; - - revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK; - - if (pdata->keymapsize) { - error = adp5589_keypad_add(kpad, revid); - if (error) - return error; - } - - error = adp5589_setup(kpad); - if (error) - return error; - - if (kpad->gpimapsize) - adp5589_report_switch_state(kpad); - - error = adp5589_gpio_add(kpad); - if (error) - return error; - - dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); - return 0; -} - -static int adp5589_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adp5589_kpad *kpad = i2c_get_clientdata(client); - - if (kpad->input) - disable_irq(client->irq); - - return 0; -} - -static int adp5589_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adp5589_kpad *kpad = i2c_get_clientdata(client); - - if (kpad->input) - enable_irq(client->irq); - - return 0; -} - -static DEFINE_SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); - -static const struct i2c_device_id adp5589_id[] = { - {"adp5589-keys", ADP5589}, - {"adp5585-keys", ADP5585_01}, - {"adp5585-02-keys", ADP5585_02}, /* Adds ROW5 to ADP5585 */ - {} -}; - -MODULE_DEVICE_TABLE(i2c, adp5589_id); - -static struct i2c_driver adp5589_driver = { - .driver = { - .name = KBUILD_MODNAME, - .pm = pm_sleep_ptr(&adp5589_dev_pm_ops), - }, - .probe = adp5589_probe, - .id_table = adp5589_id, -}; - -module_i2c_driver(adp5589_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("ADP5589/ADP5585 Keypad driver"); -- GitLab From 4bdef655542d8ed4bf3d57ea06ff128176f4927c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:13 +0100 Subject: [PATCH 348/868] mfd: adp5585: Support getting vdd regulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we get and enable the VDD supply (if available). Reviewed-by: Lee Jones Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-18-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 8f0fd73744261..11a26f6686534 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -17,6 +17,7 @@ #include #include #include +#include #include enum { @@ -709,6 +710,10 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) if (IS_ERR(regmap_config)) return PTR_ERR(regmap_config); + ret = devm_regulator_get_enable(&i2c->dev, "vdd"); + if (ret) + return ret; + adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config); if (IS_ERR(adp5585->regmap)) return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap), -- GitLab From ce262d6d629a926c8c9a2075af3b9a270ab6c641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:14 +0100 Subject: [PATCH 349/868] dt-bindings: mfd: adp5585: document reset gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a reset gpio property. Note that for the adp5585-01 models, the reset pin is used as the additional ROW5 which means there's no reset. Acked-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-19-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/adi,adp5585.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml index b3bf2ed586104..2d4ecee3f2547 100644 --- a/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml +++ b/Documentation/devicetree/bindings/mfd/adi,adp5585.yaml @@ -39,6 +39,9 @@ properties: vdd-supply: true + reset-gpios: + maxItems: 1 + gpio-controller: true '#gpio-cells': @@ -166,6 +169,7 @@ allOf: adi,unlock-events: false adi,unlock-trigger-sec: false gpio-reserved-ranges: false + reset-gpios: false adi,keypad-pins: minItems: 2 maxItems: 11 -- GitLab From 45ee66c37f9bd8cff7718c70d84e0291d385a093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Tue, 1 Jul 2025 15:32:15 +0100 Subject: [PATCH 350/868] mfd: adp5585: Add support for a reset pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure to perform an Hardware reset during probe if the pin is given in FW. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20250701-dev-adp5589-fw-v7-20-b1fcfe9e9826@analog.com Signed-off-by: Lee Jones --- drivers/mfd/adp5585.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mfd/adp5585.c b/drivers/mfd/adp5585.c index 11a26f6686534..58f7cebe2ea4f 100644 --- a/drivers/mfd/adp5585.c +++ b/drivers/mfd/adp5585.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -690,6 +691,7 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) { struct regmap_config *regmap_config; struct adp5585_dev *adp5585; + struct gpio_desc *gpio; unsigned int id; int ret; @@ -714,6 +716,20 @@ static int adp5585_i2c_probe(struct i2c_client *i2c) if (ret) return ret; + gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + /* + * Note the timings are not documented anywhere in the datasheet. They are just + * reasonable values that work. + */ + if (gpio) { + fsleep(30); + gpiod_set_value_cansleep(gpio, 0); + fsleep(60); + } + adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config); if (IS_ERR(adp5585->regmap)) return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap), -- GitLab From e47a324d6f07c9ef252cfce1f14cfa5110cbed99 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 27 Jun 2025 18:40:04 -0500 Subject: [PATCH 351/868] dt-bindings: trigger-source: add ADI Util Sigma-Delta SPI Add new binding for the ADI Util Sigma-Delta SPI FPGA IP Core. This is used to trigger a SPI offload based on a RDY signal from the ADC while masking out other signals on the same line. Reviewed-by: Rob Herring (Arm) Signed-off-by: David Lechner Link: https://patch.msgid.link/20250627-iio-adc-ad7173-add-spi-offload-support-v2-8-f49c55599113@baylibre.com Signed-off-by: Mark Brown --- .../adi,util-sigma-delta-spi.yaml | 49 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 54 insertions(+) create mode 100644 Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml diff --git a/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml new file mode 100644 index 0000000000000..ea466179551cb --- /dev/null +++ b/Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2025 Analog Devices, Inc. +# Copyright (c) 2025 BayLibre, SAS + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trigger-source/adi,util-sigma-delta-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices Util Sigma-Delta SPI IP Core + +maintainers: + - David Lechner + +description: + The Util Sigma-Delta SPI is an FPGA IP core from Analog Devices that provides + a SPI offload trigger from the RDY signal of the combined DOUT/RDY pin of + the sigma-delta family of ADCs. + https://analogdevicesinc.github.io/hdl/library/util_sigma_delta_spi/index.html + +properties: + compatible: + const: adi,util-sigma-delta-spi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#trigger-source-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - '#trigger-source-cells' + +additionalProperties: false + +examples: + - | + trigger@40000 { + reg = <0x40000 0x1000>; + compatible = "adi,util-sigma-delta-spi"; + clocks = <&clk 0>; + #trigger-source-cells = <0>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 4bac4ea21b644..d7b5e85722794 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25235,6 +25235,11 @@ W: https://github.com/srcres258/linux-doc T: git git://github.com/srcres258/linux-doc.git doc-zh-tw F: Documentation/translations/zh_TW/ +TRIGGER SOURCE - ADI UTIL SIGMA DELTA SPI +M: David Lechner +S: Maintained +F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml + TRIGGER SOURCE - PWM M: David Lechner S: Maintained -- GitLab From 3fcd3d2fe44dc9dfca20b6aed117f314a50ba0ff Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 27 Jun 2025 18:40:05 -0500 Subject: [PATCH 352/868] spi: offload trigger: add ADI Util Sigma-Delta SPI driver Add a new driver for the ADI Util Sigma-Delta SPI FPGA IP core. This is used to trigger a SPI offload based on a RDY signal from an ADC while masking out other signals on the same line. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250627-iio-adc-ad7173-add-spi-offload-support-v2-9-f49c55599113@baylibre.com Signed-off-by: Mark Brown --- MAINTAINERS | 2 +- drivers/spi/Kconfig | 5 ++ drivers/spi/Makefile | 1 + ...spi-offload-trigger-adi-util-sigma-delta.c | 59 +++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c diff --git a/MAINTAINERS b/MAINTAINERS index d7b5e85722794..d0d5b822a1f72 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23413,7 +23413,7 @@ F: include/linux/mtd/spi-nor.h SPI OFFLOAD R: David Lechner -F: drivers/spi/spi-offload-trigger-pwm.c +F: drivers/spi/spi-offload-trigger-*.c F: drivers/spi/spi-offload.c F: include/linux/spi/offload/ K: spi_offload diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc36049..e69f060d3875c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1355,6 +1355,11 @@ if SPI_OFFLOAD comment "SPI Offload triggers" +config SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD + tristate "SPI offload trigger using ADI sigma-delta utility" + help + SPI offload trigger from ADI sigma-delta utility FPGA IP block. + config SPI_OFFLOAD_TRIGGER_PWM tristate "SPI offload trigger using PWM" depends on PWM diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc5316..51f9f16ed7344 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -170,3 +170,4 @@ obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o # SPI offload triggers obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_PWM) += spi-offload-trigger-pwm.o +obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD) += spi-offload-trigger-adi-util-sigma-delta.o diff --git a/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c new file mode 100644 index 0000000000000..035d088d4d33d --- /dev/null +++ b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Analog Devices Inc. + * Copyright (C) 2025 BayLibre, SAS + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool adi_util_sigma_delta_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 0; +} + +static const struct spi_offload_trigger_ops adi_util_sigma_delta_ops = { + .match = adi_util_sigma_delta_match, +}; + +static int adi_util_sigma_delta_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_offload_trigger_info info = { + .fwnode = dev_fwnode(dev), + .ops = &adi_util_sigma_delta_ops, + }; + struct clk *clk; + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n"); + + return devm_spi_offload_trigger_register(dev, &info); +} + +static const struct of_device_id adi_util_sigma_delta_of_match_table[] = { + { .compatible = "adi,util-sigma-delta-spi", }, + { } +}; +MODULE_DEVICE_TABLE(of, adi_util_sigma_delta_of_match_table); + +static struct platform_driver adi_util_sigma_delta_driver = { + .probe = adi_util_sigma_delta_probe, + .driver = { + .name = "adi-util-sigma-delta-spi", + .of_match_table = adi_util_sigma_delta_of_match_table, + }, +}; +module_platform_driver(adi_util_sigma_delta_driver); + +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("ADI Sigma-Delta SPI offload trigger utility driver"); +MODULE_LICENSE("GPL"); -- GitLab From 34e61ba8193945c90f1bcaa9d595fc05c586663d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 10:12:22 +0200 Subject: [PATCH 353/868] gpio: pisosr: remove unneeded direction_output() callback GPIO core can handle input-only chips that don't implement the direction_output() callback at all. There's no need for the driver to provide a dummy implementation so drop it. Link: https://lore.kernel.org/r/20250625081222.12744-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pisosr.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index e3013e778e151..a69b74866a134 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -67,13 +67,6 @@ static int pisosr_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int pisosr_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - /* This device is input only */ - return -EINVAL; -} - static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset) { struct pisosr_gpio *gpio = gpiochip_get_data(chip); @@ -108,7 +101,6 @@ static const struct gpio_chip template_chip = { .owner = THIS_MODULE, .get_direction = pisosr_gpio_get_direction, .direction_input = pisosr_gpio_direction_input, - .direction_output = pisosr_gpio_direction_output, .get = pisosr_gpio_get, .get_multiple = pisosr_gpio_get_multiple, .base = -1, -- GitLab From df213abe6913cae8d1d69efa66b725831f63e663 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:24 +0200 Subject: [PATCH 354/868] gpio: sama5d2-piobu: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-1-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sama5d2-piobu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index d770a6f3d8466..c31244cf5e891 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -169,15 +169,15 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) /* * sama5d2_piobu_set() - gpiochip set */ -static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, - int value) +static int sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, + int value) { if (!value) value = PIOBU_LOW; else value = PIOBU_HIGH; - sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); + return sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value); } static int sama5d2_piobu_probe(struct platform_device *pdev) @@ -196,7 +196,7 @@ static int sama5d2_piobu_probe(struct platform_device *pdev) piobu->chip.direction_input = sama5d2_piobu_direction_input; piobu->chip.direction_output = sama5d2_piobu_direction_output; piobu->chip.get = sama5d2_piobu_get; - piobu->chip.set = sama5d2_piobu_set; + piobu->chip.set_rv = sama5d2_piobu_set; piobu->chip.base = -1; piobu->chip.ngpio = PIOBU_NUM; piobu->chip.can_sleep = 0; -- GitLab From e932e894aec6ee22d7314f74e0a27db244a14fdb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:25 +0200 Subject: [PATCH 355/868] gpio: sch311x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-2-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sch311x.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index ba4fccf3cc94f..44fb5fc21fb8a 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -178,14 +178,16 @@ static void __sch311x_gpio_set(struct sch311x_gpio_block *block, outb(data, block->runtime_reg + block->data_reg); } -static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) +static int sch311x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct sch311x_gpio_block *block = gpiochip_get_data(chip); spin_lock(&block->lock); __sch311x_gpio_set(block, offset, value); spin_unlock(&block->lock); + + return 0; } static int sch311x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) @@ -295,7 +297,7 @@ static int sch311x_gpio_probe(struct platform_device *pdev) block->chip.get_direction = sch311x_gpio_get_direction; block->chip.set_config = sch311x_gpio_set_config; block->chip.get = sch311x_gpio_get; - block->chip.set = sch311x_gpio_set; + block->chip.set_rv = sch311x_gpio_set; block->chip.ngpio = 8; block->chip.parent = &pdev->dev; block->chip.base = sch311x_gpio_blocks[i].base; -- GitLab From 883c7eb2c4a9e143b2662ba754f9c16fb31adced Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:26 +0200 Subject: [PATCH 356/868] gpio: sch: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-3-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sch.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index ff0341b1222f7..833ffdd98d744 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -117,7 +117,7 @@ static int sch_gpio_get(struct gpio_chip *gc, unsigned int gpio_num) return sch_gpio_reg_get(sch, gpio_num, GLV); } -static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) +static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) { struct sch_gpio *sch = gpiochip_get_data(gc); unsigned long flags; @@ -125,6 +125,8 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) spin_lock_irqsave(&sch->lock, flags); sch_gpio_reg_set(sch, gpio_num, GLV, val); spin_unlock_irqrestore(&sch->lock, flags); + + return 0; } static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, @@ -146,8 +148,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, * But we cannot prevent a short low pulse if direction is set to high * and an external pull-up is connected. */ - sch_gpio_set(gc, gpio_num, val); - return 0; + return sch_gpio_set(gc, gpio_num, val); } static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) @@ -166,7 +167,7 @@ static const struct gpio_chip sch_gpio_chip = { .direction_input = sch_gpio_direction_in, .get = sch_gpio_get, .direction_output = sch_gpio_direction_out, - .set = sch_gpio_set, + .set_rv = sch_gpio_set, .get_direction = sch_gpio_get_direction, }; -- GitLab From d5297b0f861a124efe7965619212a632d5138281 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:27 +0200 Subject: [PATCH 357/868] gpio: siox: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Thorsten Scherer Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-4-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-siox.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c index 051bc99bdfb2a..95355dda621b4 100644 --- a/drivers/gpio/gpio-siox.c +++ b/drivers/gpio/gpio-siox.c @@ -160,8 +160,8 @@ static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset) return ret; } -static void gpio_siox_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int gpio_siox_set(struct gpio_chip *chip, + unsigned int offset, int value) { struct gpio_siox_ddata *ddata = gpiochip_get_data(chip); u8 mask = 1 << (19 - offset); @@ -174,6 +174,8 @@ static void gpio_siox_set(struct gpio_chip *chip, ddata->setdata[0] &= ~mask; mutex_unlock(&ddata->lock); + + return 0; } static int gpio_siox_direction_input(struct gpio_chip *chip, @@ -191,8 +193,7 @@ static int gpio_siox_direction_output(struct gpio_chip *chip, if (offset < 12) return -EINVAL; - gpio_siox_set(chip, offset, value); - return 0; + return gpio_siox_set(chip, offset, value); } static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) @@ -236,7 +237,7 @@ static int gpio_siox_probe(struct siox_device *sdevice) gc->parent = dev; gc->owner = THIS_MODULE; gc->get = gpio_siox_get; - gc->set = gpio_siox_set; + gc->set_rv = gpio_siox_set; gc->direction_input = gpio_siox_direction_input; gc->direction_output = gpio_siox_direction_output; gc->get_direction = gpio_siox_get_direction; -- GitLab From e9a5f9ac245fd58b8477f1d2fe5a077803631460 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:28 +0200 Subject: [PATCH 358/868] gpio: spear-spics: remove unneeded callbacks GPIO core can handle output-only chips that don't implement the get() and direction_input() callbacks. There's no need to provide dummy implementations in the driver so drop them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-5-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-spear-spics.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 51539185400d3..964b7dcb30b7a 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -51,12 +51,6 @@ struct spear_spics { struct gpio_chip chip; }; -/* gpio framework specific routines */ -static int spics_get_value(struct gpio_chip *chip, unsigned offset) -{ - return -ENXIO; -} - static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) { struct spear_spics *spics = gpiochip_get_data(chip); @@ -76,11 +70,6 @@ static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) writel_relaxed(tmp, spics->base + spics->perip_cfg); } -static int spics_direction_input(struct gpio_chip *chip, unsigned offset) -{ - return -ENXIO; -} - static int spics_direction_output(struct gpio_chip *chip, unsigned offset, int value) { @@ -148,9 +137,7 @@ static int spics_gpio_probe(struct platform_device *pdev) spics->chip.base = -1; spics->chip.request = spics_request; spics->chip.free = spics_free; - spics->chip.direction_input = spics_direction_input; spics->chip.direction_output = spics_direction_output; - spics->chip.get = spics_get_value; spics->chip.set = spics_set_value; spics->chip.label = dev_name(&pdev->dev); spics->chip.parent = &pdev->dev; -- GitLab From 70c8f51ff68147176a41d549587a67ea377ed2e2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:29 +0200 Subject: [PATCH 359/868] gpio: spear-spics: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-6-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-spear-spics.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 964b7dcb30b7a..55f0e8afa2914 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -51,7 +51,8 @@ struct spear_spics { struct gpio_chip chip; }; -static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) +static int spics_set_value(struct gpio_chip *chip, unsigned int offset, + int value) { struct spear_spics *spics = gpiochip_get_data(chip); u32 tmp; @@ -68,13 +69,14 @@ static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) tmp &= ~(0x1 << spics->cs_value_bit); tmp |= value << spics->cs_value_bit; writel_relaxed(tmp, spics->base + spics->perip_cfg); + + return 0; } static int spics_direction_output(struct gpio_chip *chip, unsigned offset, int value) { - spics_set_value(chip, offset, value); - return 0; + return spics_set_value(chip, offset, value); } static int spics_request(struct gpio_chip *chip, unsigned offset) @@ -138,7 +140,7 @@ static int spics_gpio_probe(struct platform_device *pdev) spics->chip.request = spics_request; spics->chip.free = spics_free; spics->chip.direction_output = spics_direction_output; - spics->chip.set = spics_set_value; + spics->chip.set_rv = spics_set_value; spics->chip.label = dev_name(&pdev->dev); spics->chip.parent = &pdev->dev; spics->chip.owner = THIS_MODULE; -- GitLab From ae35dd91ad2ea4ae446e74364edd6428a26f5080 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:30 +0200 Subject: [PATCH 360/868] gpio: sprd: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Baolin Wang Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-7-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sprd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index c117c11bfb29a..bbd5bf51c0882 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -108,10 +108,12 @@ static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset) return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA); } -static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int sprd_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value); + + return 0; } static void sprd_gpio_irq_mask(struct irq_data *data) @@ -243,7 +245,7 @@ static int sprd_gpio_probe(struct platform_device *pdev) sprd_gpio->chip.request = sprd_gpio_request; sprd_gpio->chip.free = sprd_gpio_free; sprd_gpio->chip.get = sprd_gpio_get; - sprd_gpio->chip.set = sprd_gpio_set; + sprd_gpio->chip.set_rv = sprd_gpio_set; sprd_gpio->chip.direction_input = sprd_gpio_direction_input; sprd_gpio->chip.direction_output = sprd_gpio_direction_output; -- GitLab From c9148553ac13565ad06d83d7baebef133245ebe6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:31 +0200 Subject: [PATCH 361/868] gpio: stmpe: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-8-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-stmpe.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dce8ff322e472..0a270156e0bea 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -54,7 +54,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(ret & mask); } -static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int stmpe_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; @@ -67,9 +67,9 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) * For them we need to write 0 to clear and 1 to set. */ if (stmpe->regs[STMPE_IDX_GPSR_LSB] == stmpe->regs[STMPE_IDX_GPCR_LSB]) - stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); - else - stmpe_reg_write(stmpe, reg, mask); + return stmpe_set_bits(stmpe, reg, mask, val ? mask : 0); + + return stmpe_reg_write(stmpe, reg, mask); } static int stmpe_gpio_get_direction(struct gpio_chip *chip, @@ -98,8 +98,11 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip, struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; u8 mask = BIT(offset % 8); + int ret; - stmpe_gpio_set(chip, offset, val); + ret = stmpe_gpio_set(chip, offset, val); + if (ret) + return ret; return stmpe_set_bits(stmpe, reg, mask, mask); } @@ -133,7 +136,7 @@ static const struct gpio_chip template_chip = { .direction_input = stmpe_gpio_direction_input, .get = stmpe_gpio_get, .direction_output = stmpe_gpio_direction_output, - .set = stmpe_gpio_set, + .set_rv = stmpe_gpio_set, .request = stmpe_gpio_request, .can_sleep = true, }; -- GitLab From e87dff29ff6b919f64ca25b066c44bbacdc08ac3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:32 +0200 Subject: [PATCH 362/868] gpio: stp-xway: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-9-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-stp-xway.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-stp-xway.c b/drivers/gpio/gpio-stp-xway.c index 5a6406d1f03aa..fdda8de6ca366 100644 --- a/drivers/gpio/gpio-stp-xway.c +++ b/drivers/gpio/gpio-stp-xway.c @@ -113,7 +113,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio) * * Set the shadow value and call ltq_ebu_apply. */ -static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) +static int xway_stp_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct xway_stp *chip = gpiochip_get_data(gc); @@ -124,6 +124,8 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) xway_stp_w32(chip->virt, chip->shadow, XWAY_STP_CPU0); if (!chip->reserved) xway_stp_w32_mask(chip->virt, 0, XWAY_STP_CON_SWU, XWAY_STP_CON0); + + return 0; } /** @@ -136,9 +138,7 @@ static void xway_stp_set(struct gpio_chip *gc, unsigned gpio, int val) */ static int xway_stp_dir_out(struct gpio_chip *gc, unsigned gpio, int val) { - xway_stp_set(gc, gpio, val); - - return 0; + return xway_stp_set(gc, gpio, val); } /** @@ -249,7 +249,7 @@ static int xway_stp_probe(struct platform_device *pdev) chip->gc.label = "stp-xway"; chip->gc.direction_output = xway_stp_dir_out; chip->gc.get = xway_stp_get; - chip->gc.set = xway_stp_set; + chip->gc.set_rv = xway_stp_set; chip->gc.request = xway_stp_request; chip->gc.base = -1; chip->gc.owner = THIS_MODULE; -- GitLab From c203705c9b46ad0b66ef3bdc93ec9073b00efed1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:33 +0200 Subject: [PATCH 363/868] gpio: syscon: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-10-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-syscon.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 5ab394ec81e69..f86f78655c242 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -40,8 +40,8 @@ struct syscon_gpio_data { unsigned int bit_count; unsigned int dat_bit_offset; unsigned int dir_bit_offset; - void (*set)(struct gpio_chip *chip, - unsigned offset, int value); + int (*set)(struct gpio_chip *chip, unsigned int offset, + int value); }; struct syscon_gpio_priv { @@ -68,17 +68,17 @@ static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offs % SYSCON_REG_BITS)); } -static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int syscon_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; - regmap_update_bits(priv->syscon, - (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, - BIT(offs % SYSCON_REG_BITS), - val ? BIT(offs % SYSCON_REG_BITS) : 0); + return regmap_update_bits(priv->syscon, + (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, + BIT(offs % SYSCON_REG_BITS), + val ? BIT(offs % SYSCON_REG_BITS) : 0); } static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset) @@ -115,9 +115,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val) BIT(offs % SYSCON_REG_BITS)); } - chip->set(chip, offset, val); - - return 0; + return chip->set_rv(chip, offset, val); } static const struct syscon_gpio_data clps711x_mctrl_gpio = { @@ -127,8 +125,8 @@ static const struct syscon_gpio_data clps711x_mctrl_gpio = { .dat_bit_offset = 0x40 * 8 + 8, }; -static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, - int val) +static int rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; @@ -144,6 +142,8 @@ static void rockchip_gpio_set(struct gpio_chip *chip, unsigned int offset, data); if (ret < 0) dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + + return ret; } static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = { @@ -156,7 +156,8 @@ static const struct syscon_gpio_data rockchip_rk3328_gpio_mute = { #define KEYSTONE_LOCK_BIT BIT(0) -static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int keystone_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct syscon_gpio_priv *priv = gpiochip_get_data(chip); unsigned int offs; @@ -165,7 +166,7 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) offs = priv->dreg_offset + priv->data->dat_bit_offset + offset; if (!val) - return; + return 0; ret = regmap_update_bits( priv->syscon, @@ -174,6 +175,8 @@ static void keystone_gpio_set(struct gpio_chip *chip, unsigned offset, int val) BIT(offs % SYSCON_REG_BITS) | KEYSTONE_LOCK_BIT); if (ret < 0) dev_err(chip->parent, "gpio write failed ret(%d)\n", ret); + + return ret; } static const struct syscon_gpio_data keystone_dsp_gpio = { @@ -248,7 +251,7 @@ static int syscon_gpio_probe(struct platform_device *pdev) if (priv->data->flags & GPIO_SYSCON_FEAT_IN) priv->chip.direction_input = syscon_gpio_dir_in; if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) { - priv->chip.set = priv->data->set ? : syscon_gpio_set; + priv->chip.set_rv = priv->data->set ? : syscon_gpio_set; priv->chip.direction_output = syscon_gpio_dir_out; } -- GitLab From f3c9b6a51cb31a8816feb801c8c8a2265432143e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:34 +0200 Subject: [PATCH 364/868] gpio: tangier: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-11-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tangier.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-tangier.c b/drivers/gpio/gpio-tangier.c index a415e6d361731..ce17b98e0623e 100644 --- a/drivers/gpio/gpio-tangier.c +++ b/drivers/gpio/gpio-tangier.c @@ -90,7 +90,7 @@ static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(readl(gplr) & BIT(shift)); } -static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct tng_gpio *priv = gpiochip_get_data(chip); void __iomem *reg; @@ -101,6 +101,8 @@ static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) guard(raw_spinlock_irqsave)(&priv->lock); writel(BIT(shift), reg); + + return 0; } static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -428,7 +430,7 @@ int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio) gpio->chip.direction_input = tng_gpio_direction_input; gpio->chip.direction_output = tng_gpio_direction_output; gpio->chip.get = tng_gpio_get; - gpio->chip.set = tng_gpio_set; + gpio->chip.set_rv = tng_gpio_set; gpio->chip.get_direction = tng_gpio_get_direction; gpio->chip.set_config = tng_gpio_set_config; gpio->chip.base = info->base; -- GitLab From b033bc5a9a7d95b8dc206dd7455a033b0670d8e7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 25 Jun 2025 12:33:35 +0200 Subject: [PATCH 365/868] gpio: tc3589x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-12-bc110a3b52ff@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tc3589x.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index e62ee7e56908f..0bd32809fd682 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -49,7 +49,7 @@ static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(ret & mask); } -static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) +static int tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; @@ -57,7 +57,7 @@ static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int va unsigned int pos = offset % 8; u8 data[] = {val ? BIT(pos) : 0, BIT(pos)}; - tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); + return tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); } static int tc3589x_gpio_direction_output(struct gpio_chip *chip, @@ -67,8 +67,11 @@ static int tc3589x_gpio_direction_output(struct gpio_chip *chip, struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODIR0 + offset / 8; unsigned int pos = offset % 8; + int ret; - tc3589x_gpio_set(chip, offset, val); + ret = tc3589x_gpio_set(chip, offset, val); + if (ret) + return ret; return tc3589x_set_bits(tc3589x, reg, BIT(pos), BIT(pos)); } @@ -146,7 +149,7 @@ static const struct gpio_chip template_chip = { .label = "tc3589x", .owner = THIS_MODULE, .get = tc3589x_gpio_get, - .set = tc3589x_gpio_set, + .set_rv = tc3589x_gpio_set, .direction_output = tc3589x_gpio_direction_output, .direction_input = tc3589x_gpio_direction_input, .get_direction = tc3589x_gpio_get_direction, -- GitLab From a3b0e80428c8e2aa50d7e3c75721dd7f5c76c4d9 Mon Sep 17 00:00:00 2001 From: Venkata Prasad Potturu Date: Wed, 2 Jul 2025 15:14:10 +0530 Subject: [PATCH 366/868] ASoC: amd: acp: Add legacy driver support acp7.2 based platforms Add pci revision id 0x72 in pci and platform driver to support acp7.2 based platforms. Signed-off-by: Venkata Prasad Potturu Link: https://patch.msgid.link/20250702094425.155185-2-venkataprasad.potturu@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-legacy-common.c | 2 ++ sound/soc/amd/acp/acp-pci.c | 1 + sound/soc/amd/acp/acp-platform.c | 2 ++ sound/soc/amd/acp/acp70.c | 1 + sound/soc/amd/acp/acp_common.h | 1 + 5 files changed, 7 insertions(+) diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index ba8db0851daac..3078f459e0050 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -372,6 +372,7 @@ static int acp_power_on(struct acp_chip_info *chip) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS; acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL; break; @@ -573,6 +574,7 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: pdm_addr = ACP70_PDM_ADDR; check_acp70_config(chip); break; diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 2591b1a1c5e00..f83708755ed15 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -153,6 +153,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id break; case 0x70: case 0x71: + case 0x72: chip->name = "acp_asoc_acp70"; chip->rsrc = &acp70_rsrc; chip->acp_hw_ops_init = acp70_hw_ops_init; diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index b3eddf76aaa41..b25ac5612808c 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -140,6 +140,7 @@ void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int s switch (chip->acp_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: switch (stream->dai_id) { case I2S_SP_INSTANCE: if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -205,6 +206,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = acp6x_pcm_hardware_playback; else diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index b95e3949e70be..bca311c88139d 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -136,6 +136,7 @@ static int acp_acp70_audio_probe(struct platform_device *pdev) switch (chip->acp_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: break; default: dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); diff --git a/sound/soc/amd/acp/acp_common.h b/sound/soc/amd/acp/acp_common.h index f1ae88013f629..984685602e3d2 100644 --- a/sound/soc/amd/acp/acp_common.h +++ b/sound/soc/amd/acp/acp_common.h @@ -15,5 +15,6 @@ #define ACP63_PCI_ID 0x63 #define ACP70_PCI_ID 0x70 #define ACP71_PCI_ID 0x71 +#define ACP72_PCI_ID 0x72 #endif -- GitLab From 3549725e0f7823d085403fc4219fd3df347a1ae4 Mon Sep 17 00:00:00 2001 From: Venkata Prasad Potturu Date: Wed, 2 Jul 2025 15:14:11 +0530 Subject: [PATCH 367/868] ASoC: amd: acp: Enable I2S support for acp7.2 based platforms Enable I2S dai driver support for acp7.2 based platforms. Signed-off-by: Venkata Prasad Potturu Link: https://patch.msgid.link/20250702094425.155185-3-venkataprasad.potturu@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-i2s.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 70fa54d568ef6..617690362ad75 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -58,6 +58,7 @@ static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id) case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div); val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div); break; @@ -134,6 +135,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: switch (slots) { case 1 ... 31: no_of_slots = slots; @@ -168,6 +170,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) chip->tdm_tx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 13) | (slot_len << 18); -- GitLab From 0c0ef1d90967717b91cded41b00dbae05d8e521c Mon Sep 17 00:00:00 2001 From: Venkata Prasad Potturu Date: Wed, 2 Jul 2025 15:14:12 +0530 Subject: [PATCH 368/868] ASoC: amd: acp: Enable acp7.2 platform based DMIC support in machine driver Enable acp7.2 platform based DMIC support in machine driver. Signed-off-by: Venkata Prasad Potturu Link: https://patch.msgid.link/20250702094425.155185-4-venkataprasad.potturu@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index a0dab85088ec5..c4bc8e849284c 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -1772,6 +1772,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card) break; case ACP70_PCI_ID: case ACP71_PCI_ID: + case ACP72_PCI_ID: links[i].platforms = platform_acp70_component; links[i].num_platforms = ARRAY_SIZE(platform_acp70_component); break; -- GitLab From 3b8dc31715e31ab930d36ef7b98ffc714344e411 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 1 Jul 2025 15:59:14 +0530 Subject: [PATCH 369/868] ASoC: dt-bindings: qcom,sm8250: Add QCS8275 sound card Add bindings for QCS8275 sound card, which looks fully compatible with existing SM8250. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20250701102915.4016108-2-quic_pkumpatl@quicinc.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 590eb177f57ab..6b4a8dbdaf615 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -32,6 +32,7 @@ properties: - qcom,apq8096-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs6490-rb3gen2-sndcard + - qcom,qcs8275-sndcard - qcom,qcs9075-sndcard - qcom,qcs9100-sndcard - qcom,qrb4210-rb2-sndcard -- GitLab From 34d340d48e595f8dfd4e72fe4100d2579dbe4a1a Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Tue, 1 Jul 2025 15:59:15 +0530 Subject: [PATCH 370/868] ASoC: qcom: sc8280xp: Add support for QCS8275 Add compatible for sound card on Qualcomm QCS8275 boards. Signed-off-by: Prasad Kumpatla Link: https://patch.msgid.link/20250701102915.4016108-3-quic_pkumpatl@quicinc.com Signed-off-by: Mark Brown --- sound/soc/qcom/sc8280xp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 99fd34728e387..73f9f82c4e258 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -186,6 +186,7 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) static const struct of_device_id snd_sc8280xp_dt_match[] = { {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, + {.compatible = "qcom,qcs8275-sndcard", "qcs8275"}, {.compatible = "qcom,qcs9075-sndcard", "qcs9075"}, {.compatible = "qcom,qcs9100-sndcard", "qcs9100"}, {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, -- GitLab From 30cd59961981ae235c59aca61715f6732f65d8cc Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 1 Jul 2025 14:05:25 +0200 Subject: [PATCH 371/868] ALSA: pcmtest: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250701120525.185831-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/drivers/pcmtest.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 39f1e1fe4c446..19b3f306c5645 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -555,7 +556,7 @@ static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst) if (err < 0) return err; pcm->private_data = pcmtst; - strcpy(pcm->name, "PCMTest"); + strscpy(pcm->name, "PCMTest"); pcmtst->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcmtst_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcmtst_capture_ops); @@ -613,9 +614,9 @@ static int pcmtst_probe(struct platform_device *pdev) if (err < 0) return err; - strcpy(card->driver, "PCM-TEST Driver"); - strcpy(card->shortname, "PCM-Test"); - strcpy(card->longname, "PCM-Test virtual driver"); + strscpy(card->driver, "PCM-TEST Driver"); + strscpy(card->shortname, "PCM-Test"); + strscpy(card->longname, "PCM-Test virtual driver"); err = snd_card_register(card); if (err < 0) -- GitLab From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 2 Jul 2025 14:35:23 +0200 Subject: [PATCH 372/868] mtd: nand: qpic-common: add defines for ECC_MODE values Add defines for the values of the ECC_MODE field of the NAND_DEV0_ECC_CFG register and change both the 'qcom-nandc' and 'spi-qpic-snand' drivers to use those instead of magic numbers. No functional changes. This is in preparation for adding 8 bit ECC strength support for the 'spi-qpic-snand' driver. Reviewed-by: Md Sadre Alam Signed-off-by: Gabor Juhos Acked-by: Miquel Raynal Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com Signed-off-by: Mark Brown --- drivers/mtd/nand/raw/qcom_nandc.c | 6 +++--- drivers/spi/spi-qpic-snand.c | 2 +- include/linux/mtd/nand-qpic-common.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 1003cf118c01b..4dd6f1a4e7978 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); int cwperpage, bad_block_byte, ret; bool wide_bus; - int ecc_mode = 1; + int ecc_mode = ECC_MODE_8BIT; /* controller only supports 512 bytes data steps */ ecc->size = NANDC_STEP_SIZE; @@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) if (ecc->strength >= 8) { /* 8 bit ECC defaults to BCH ECC on all platforms */ host->bch_enabled = true; - ecc_mode = 1; + ecc_mode = ECC_MODE_8BIT; if (wide_bus) { host->ecc_bytes_hw = 14; @@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) if (nandc->props->ecc_modes & ECC_BCH_4BIT) { /* BCH */ host->bch_enabled = true; - ecc_mode = 0; + ecc_mode = ECC_MODE_4BIT; if (wide_bus) { host->ecc_bytes_hw = 8; diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index ca55f9bcd17b4..7219bcaf4055a 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -343,7 +343,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) FIELD_PREP(ECC_SW_RESET, 0) | FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | - FIELD_PREP(ECC_MODE_MASK, 0) | + FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203); diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h index e8462deda6dbf..0d944db363cd2 100644 --- a/include/linux/mtd/nand-qpic-common.h +++ b/include/linux/mtd/nand-qpic-common.h @@ -101,6 +101,8 @@ #define ECC_SW_RESET BIT(1) #define ECC_MODE 4 #define ECC_MODE_MASK GENMASK(5, 4) +#define ECC_MODE_4BIT 0 +#define ECC_MODE_8BIT 1 #define ECC_PARITY_SIZE_BYTES_BCH 8 #define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8) #define ECC_NUM_DATA_BYTES 16 -- GitLab From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 2 Jul 2025 14:35:24 +0200 Subject: [PATCH 373/868] spi: spi-qpic-snand: add support for 8 bits ECC strength Even though the hardware supports 8 bits ECC strength, but that is not handled in the driver yet. This change adds the missing bits in order to allow using the driver with chips which require 8 bits ECC strength. No functional changes intended with regard to the existing 4 bits ECC strength support. Tested on an IPQ9574 platform using a GigaDevice GD5F2GM7REYIG chip. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 7219bcaf4055a..b711c8be42c1f 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -277,9 +277,22 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) goto err_free_ecc_cfg; } - if (ecc_cfg->strength != 4) { + switch (ecc_cfg->strength) { + case 4: + ecc_cfg->ecc_mode = ECC_MODE_4BIT; + ecc_cfg->ecc_bytes_hw = 7; + ecc_cfg->spare_bytes = 4; + break; + + case 8: + ecc_cfg->ecc_mode = ECC_MODE_8BIT; + ecc_cfg->ecc_bytes_hw = 13; + ecc_cfg->spare_bytes = 2; + break; + + default: dev_err(snandc->dev, - "only 4 bits ECC strength is supported\n"); + "only 4 or 8 bits ECC strength is supported\n"); ret = -EOPNOTSUPP; goto err_free_ecc_cfg; } @@ -296,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) nand->ecc.ctx.priv = ecc_cfg; snandc->qspi->mtd = mtd; - ecc_cfg->ecc_bytes_hw = 7; - ecc_cfg->spare_bytes = 4; ecc_cfg->bbm_size = 1; ecc_cfg->bch_enabled = true; ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; @@ -343,7 +354,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) FIELD_PREP(ECC_SW_RESET, 0) | FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) | FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) | - FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) | + FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) | FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203); -- GitLab From 4a89166ee0750b4bd9edc6f386f5ed9a49f36897 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 16 Jun 2025 20:20:28 +0200 Subject: [PATCH 374/868] ACPI: PM: Set .detach in acpi_general_pm_domain definition Instead of setting the .detach callback pointer for acpi_general_pm_domain every time it is attached to a device, which is confusing, set it once in the definition of acpi_general_pm_domain. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Link: https://patch.msgid.link/4665476.LvFx2qVVIh@rjwysocki.net Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index dbd4446025ecd..170b8eeaefe2f 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -1362,6 +1362,8 @@ static int acpi_subsys_poweroff_noirq(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ +static void acpi_dev_pm_detach(struct device *dev, bool power_off); + static struct dev_pm_domain acpi_general_pm_domain = { .ops = { .runtime_suspend = acpi_subsys_runtime_suspend, @@ -1382,6 +1384,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .restore_early = acpi_subsys_restore_early, #endif }, + .detach = acpi_dev_pm_detach, }; /** @@ -1465,7 +1468,6 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) acpi_device_wakeup_disable(adev); } - dev->pm_domain->detach = acpi_dev_pm_detach; return 1; } EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); -- GitLab From 66c1f381d8b3282a191e8d93faac938647dfbc70 Mon Sep 17 00:00:00 2001 From: Abdelrahman Fekry Date: Sat, 21 Jun 2025 08:52:00 +0300 Subject: [PATCH 375/868] ACPI: fan: Replace sprintf()/scnprintf() with sysfs_emit() in show() functions Update two sysfs show() functions in the ACPI fan driver to use sysfs_emit() and sysfs_emit_at() instead of sprintf() and scnprintf(). - show_fan_speed(): replaced sprintf() with sysfs_emit(). - show_state(): replaced scnprintf() with sysfs_emit() for the first write, and retained sysfs_emit_at() for incremental writes. This change is in accordance with Documentation/filesystems/sysfs.rst, which recommends using sysfs_emit/sysfs_emit_at in all sysfs show() callbacks for buffer safety, clarity, and consistency. Signed-off-by: Abdelrahman Fekry Link: https://patch.msgid.link/20250621055200.166361-1-abdelrahmanfekry375@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan_attr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c index 22d29ac2447cc..6a53da3d6d826 100644 --- a/drivers/acpi/fan_attr.c +++ b/drivers/acpi/fan_attr.c @@ -22,9 +22,9 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha int count; if (fps->control == 0xFFFFFFFF || fps->control > 100) - count = scnprintf(buf, PAGE_SIZE, "not-defined:"); + count = sysfs_emit(buf, "not-defined:"); else - count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); + count = sysfs_emit(buf, "%lld:", fps->control); if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) count += sysfs_emit_at(buf, count, "not-defined:"); @@ -59,7 +59,7 @@ static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, if (status) return status; - return sprintf(buf, "%lld\n", fst.speed); + return sysfs_emit(buf, "%lld\n", fst.speed); } static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) -- GitLab From 904cf14f9135a18c9efe8327362f5dfd3c0f3a3f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jun 2025 16:01:09 +0300 Subject: [PATCH 376/868] Documentation: firmware-guide: gpio-properties: Spelling and style fixes - Use consistent style for active-high and active-low - For C and ASL code snippets use 4-space indentation consistently - Interleave case examples with the explanations of the certain case - Remove or add commas when appropriate Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Link: https://patch.msgid.link/20250626130109.215848-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- .../firmware-guide/acpi/gpio-properties.rst | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Documentation/firmware-guide/acpi/gpio-properties.rst b/Documentation/firmware-guide/acpi/gpio-properties.rst index db0c0b1f37008..5addf7aaa8334 100644 --- a/Documentation/firmware-guide/acpi/gpio-properties.rst +++ b/Documentation/firmware-guide/acpi/gpio-properties.rst @@ -6,7 +6,7 @@ _DSD Device Properties Related to GPIO With the release of ACPI 5.1, the _DSD configuration object finally allows names to be given to GPIOs (and other things as well) returned -by _CRS. Previously, we were only able to use an integer index to find +by _CRS. Previously we were only able to use an integer index to find the corresponding GPIO, which is pretty error prone (it depends on the _CRS output ordering, for example). @@ -49,11 +49,11 @@ index pin Pin in the GpioIo()/GpioInt() resource. Typically this is zero. active_low - If 1, the GPIO is marked as active_low. + If 1, the GPIO is marked as active-low. Since ACPI GpioIo() resource does not have a field saying whether it is -active low or high, the "active_low" argument can be used here. Setting -it to 1 marks the GPIO as active low. +active-low or active-high, the "active_low" argument can be used here. +Setting it to 1 marks the GPIO as active-low. Note, active_low in _DSD does not make sense for GpioInt() resource and must be 0. GpioInt() resource has its own means of defining it. @@ -231,8 +231,8 @@ In those cases ACPI device identification objects, _HID, _CID, _CLS, _SUB, _HRV, available to the driver can be used to identify the device and that is supposed to be sufficient to determine the meaning and purpose of all of the GPIO lines listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words, -the driver is supposed to know what to use the GpioIo()/GpioInt() resources for -once it has identified the device. Having done that, it can simply assign names +the driver is supposed to know what to use from the GpioIo()/GpioInt() resources +for once it has identified the device. Having done that, it can simply assign names to the GPIO lines it is going to use and provide the GPIO subsystem with a mapping between those names and the ACPI GPIO resources corresponding to them. @@ -252,9 +252,9 @@ question would look like this:: static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false }; static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = { - { "reset-gpios", &reset_gpio, 1 }, - { "shutdown-gpios", &shutdown_gpio, 1 }, - { } + { "reset-gpios", &reset_gpio, 1 }, + { "shutdown-gpios", &shutdown_gpio, 1 }, + { } }; Next, the mapping table needs to be passed as the second argument to @@ -270,7 +270,7 @@ Using the _CRS fallback If a device does not have _DSD or the driver does not create ACPI GPIO mapping, the Linux GPIO framework refuses to return any GPIOs. This is -because the driver does not know what it actually gets. For example if we +because the driver does not know what it actually gets. For example, if we have a device like below:: Device (BTH) @@ -292,7 +292,7 @@ The driver might expect to get the right GPIO when it does:: ...error handling... but since there is no way to know the mapping between "reset" and -the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT). +the GpioIo() in _CRS the desc will hold ERR_PTR(-ENOENT). The driver author can solve this by passing the mapping explicitly (this is the recommended way and it's documented in the above chapter). @@ -318,15 +318,15 @@ Case 1:: desc = gpiod_get(dev, "non-null-connection-id", flags); desc = gpiod_get_index(dev, "non-null-connection-id", index, flags); +Case 1 assumes that corresponding ACPI device description must have +defined device properties and will prevent from getting any GPIO resources +otherwise. + Case 2:: desc = gpiod_get(dev, NULL, flags); desc = gpiod_get_index(dev, NULL, index, flags); -Case 1 assumes that corresponding ACPI device description must have -defined device properties and will prevent to getting any GPIO resources -otherwise. - Case 2 explicitly tells GPIO core to look for resources in _CRS. Be aware that gpiod_get_index() in cases 1 and 2, assuming that there -- GitLab From 151c1f989bcbe5972b9c26b04f10de3c47eb177c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jun 2025 16:26:05 +0300 Subject: [PATCH 377/868] ACPI: LPSS: Remove AudioDSP related ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AudioDSP drivers are in control for all functions of the hardware they have (they are multi-functional devices). The LPSS driver prepares for enumeration only single devices, such as DMA, UART, SPI, I²C. Hence the registration of AudioDSP should not be covered. Moreover, the very same ACPI _HID has been added by the catpt driver a few years ago. And even more serious issue with this, is that the register window at offset 0x800 is actually D-SRAM0 in case of AudioDSP and writing to it is a data corruption. That all being said, remove the AudioDSP ID from the LPSS driver, where it doesn't belong to. Fixes: c2f8783fa2d0 ("ASoC: Intel: Add common SST driver loader on ACPI systems") Reviewed-by: Cezary Rojewski Tested-by: Cezary Rojewski Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250626132635.221064-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/lpss.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c index 258440b899a95..6daa6372f9800 100644 --- a/drivers/acpi/x86/lpss.c +++ b/drivers/acpi/x86/lpss.c @@ -387,9 +387,6 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, - /* Wildcat Point LPSS devices */ - { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) }, - { } }; -- GitLab From 57139e126a30ce64f111c78b1b9e37b39a2b7424 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:21 -0700 Subject: [PATCH 378/868] platform/x86: Add lenovo-wmi-* driver Documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds documentation for new lenovo-wmi drivers. Reviewed-by: Alok Tiwari Reviewed-by: Armin Wolf Reviewed-by: Mario Limonciello Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-2-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/lenovo-wmi-gamezone.rst | 203 ++++++++++++++++++ .../wmi/devices/lenovo-wmi-other.rst | 108 ++++++++++ MAINTAINERS | 3 + 3 files changed, 314 insertions(+) create mode 100644 Documentation/wmi/devices/lenovo-wmi-gamezone.rst create mode 100644 Documentation/wmi/devices/lenovo-wmi-other.rst diff --git a/Documentation/wmi/devices/lenovo-wmi-gamezone.rst b/Documentation/wmi/devices/lenovo-wmi-gamezone.rst new file mode 100644 index 0000000000000..997263e51a7df --- /dev/null +++ b/Documentation/wmi/devices/lenovo-wmi-gamezone.rst @@ -0,0 +1,203 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +========================================================== +Lenovo WMI Interface Gamezone Driver (lenovo-wmi-gamezone) +========================================================== + +Introduction +============ +The Lenovo WMI gamezone interface is broken up into multiple GUIDs, +The primary "Gamezone" GUID provides advanced features such as fan +profiles and overclocking. It is paired with multiple event GUIDs +and data block GUIDs that provide context for the various methods. + +Gamezone Data +------------- + +WMI GUID ``887B54E3-DDDC-4B2C-8B88-68A26A8835D0`` + +The Gamezone Data WMI interface provides platform-profile and fan curve +settings for devices that fall under the "Gaming Series" of Lenovo devices. +It uses a notifier chain to inform other Lenovo WMI interface drivers of the +current platform profile when it changes. + +The following platform profiles are supported: + - low-power + - balanced + - balanced-performance + - performance + - custom + +Balanced-Performance +~~~~~~~~~~~~~~~~~~~~ +Some newer Lenovo "Gaming Series" laptops have an "Extreme Mode" profile +enabled in their BIOS. For these devices, the performance platform profile +corresponds to the BIOS Extreme Mode, while the balanced-performance +platform profile corresponds to the BIOS Performance mode. For legacy +devices, the performance platform profile will correspond with the BIOS +Performance mode. + +For some newer devices the "Extreme Mode" profile is incomplete in the BIOS +and setting it will cause undefined behavior. A BIOS bug quirk table is +provided to ensure these devices cannot set "Extreme Mode" from the driver. + +Custom Profile +~~~~~~~~~~~~~~ +The custom profile represents a hardware mode on Lenovo devices that enables +user modifications to Package Power Tracking (PPT) and fan curve settings. +When an attribute exposed by the Other Mode WMI interface is to be modified, +the Gamezone driver must first be switched to the "custom" profile manually, +or the setting will have no effect. If another profile is set from the list +of supported profiles, the BIOS will override any user PPT settings when +switching to that profile. + +Gamezone Thermal Mode Event +--------------------------- + +WMI GUID ``D320289E-8FEA-41E0-86F9-911D83151B5F`` + +The Gamezone Thermal Mode Event interface notifies the system when the platform +profile has changed, either through the hardware event (Fn+Q for laptops or +Legion + Y for Go Series), or through the Gamezone WMI interface. This event is +implemented in the Lenovo WMI Events driver (lenovo-wmi-events). + + +WMI interface description +========================= + +The WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_GAMEZONE_DATA class"), guid("{887B54E3-DDDC-4B2C-8B88-68A26A8835D0}")] + class LENOVO_GAMEZONE_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(4), Implemented, Description("Is SupportGpu OverClock")] void IsSupportGpuOC([out, Description("Is SupportGpu OverClock")] uint32 Data); + [WmiMethodId(11), Implemented, Description("Get AslCode Version")] void GetVersion ([out, Description("AslCode version")] UINT32 Data); + [WmiMethodId(12), Implemented, Description("Fan cooling capability")] void IsSupportFanCooling([out, Description("Fan cooling capability")] UINT32 Data); + [WmiMethodId(13), Implemented, Description("Set Fan cooling on/off")] void SetFanCooling ([in, Description("Set Fan cooling on/off")] UINT32 Data); + [WmiMethodId(14), Implemented, Description("cpu oc capability")] void IsSupportCpuOC ([out, Description("cpu oc capability")] UINT32 Data); + [WmiMethodId(15), Implemented, Description("bios has overclock capability")] void IsBIOSSupportOC ([out, Description("bios has overclock capability")] UINT32 Data); + [WmiMethodId(16), Implemented, Description("enable or disable overclock in bios")] void SetBIOSOC ([in, Description("enable or disable overclock in bios")] UINT32 Data); + [WmiMethodId(18), Implemented, Description("Get CPU temperature")] void GetCPUTemp ([out, Description("Get CPU temperature")] UINT32 Data); + [WmiMethodId(19), Implemented, Description("Get GPU temperature")] void GetGPUTemp ([out, Description("Get GPU temperature")] UINT32 Data); + [WmiMethodId(20), Implemented, Description("Get Fan cooling on/off status")] void GetFanCoolingStatus ([out, Description("Get Fan cooling on/off status")] UINT32 Data); + [WmiMethodId(21), Implemented, Description("EC support disable windows key capability")] void IsSupportDisableWinKey ([out, Description("EC support disable windows key capability")] UINT32 Data); + [WmiMethodId(22), Implemented, Description("Set windows key disable/enable")] void SetWinKeyStatus ([in, Description("Set windows key disable/enable")] UINT32 Data); + [WmiMethodId(23), Implemented, Description("Get windows key disable/enable status")] void GetWinKeyStatus ([out, Description("Get windows key disable/enable status")] UINT32 Data); + [WmiMethodId(24), Implemented, Description("EC support disable touchpad capability")] void IsSupportDisableTP ([out, Description("EC support disable touchpad capability")] UINT32 Data); + [WmiMethodId(25), Implemented, Description("Set touchpad disable/enable")] void SetTPStatus ([in, Description("Set touchpad disable/enable")] UINT32 Data); + [WmiMethodId(26), Implemented, Description("Get touchpad disable/enable status")] void GetTPStatus ([out, Description("Get touchpad disable/enable status")] UINT32 Data); + [WmiMethodId(30), Implemented, Description("Get Keyboard feature list")] void GetKeyboardfeaturelist ([out, Description("Get Keyboard feature list")] UINT32 Data); + [WmiMethodId(31), Implemented, Description("Get Memory OC Information")] void GetMemoryOCInfo ([out, Description("Get Memory OC Information")] UINT32 Data); + [WmiMethodId(32), Implemented, Description("Water Cooling feature capability")] void IsSupportWaterCooling ([out, Description("Water Cooling feature capability")] UINT32 Data); + [WmiMethodId(33), Implemented, Description("Set Water Cooling status")] void SetWaterCoolingStatus ([in, Description("Set Water Cooling status")] UINT32 Data); + [WmiMethodId(34), Implemented, Description("Get Water Cooling status")] void GetWaterCoolingStatus ([out, Description("Get Water Cooling status")] UINT32 Data); + [WmiMethodId(35), Implemented, Description("Lighting feature capability")] void IsSupportLightingFeature ([out, Description("Lighting feature capability")] UINT32 Data); + [WmiMethodId(36), Implemented, Description("Set keyboard light off or on to max")] void SetKeyboardLight ([in, Description("keyboard light off or on switch")] UINT32 Data); + [WmiMethodId(37), Implemented, Description("Get keyboard light on/off status")] void GetKeyboardLight ([out, Description("Get keyboard light on/off status")] UINT32 Data); + [WmiMethodId(38), Implemented, Description("Get Macrokey scan code")] void GetMacrokeyScancode ([in, Description("Macrokey index")] UINT32 idx, [out, Description("Scan code")] UINT32 scancode); + [WmiMethodId(39), Implemented, Description("Get Macrokey count")] void GetMacrokeyCount ([out, Description("Macrokey count")] UINT32 Data); + [WmiMethodId(40), Implemented, Description("Support G-Sync feature")] void IsSupportGSync ([out, Description("Support G-Sync feature")] UINT32 Data); + [WmiMethodId(41), Implemented, Description("Get G-Sync Status")] void GetGSyncStatus ([out, Description("Get G-Sync Status")] UINT32 Data); + [WmiMethodId(42), Implemented, Description("Set G-Sync Status")] void SetGSyncStatus ([in, Description("Set G-Sync Status")] UINT32 Data); + [WmiMethodId(43), Implemented, Description("Support Smart Fan feature")] void IsSupportSmartFan ([out, Description("Support Smart Fan feature")] UINT32 Data); + [WmiMethodId(44), Implemented, Description("Set Smart Fan Mode")] void SetSmartFanMode ([in, Description("Set Smart Fan Mode")] UINT32 Data); + [WmiMethodId(45), Implemented, Description("Get Smart Fan Mode")] void GetSmartFanMode ([out, Description("Get Smart Fan Mode")] UINT32 Data); + [WmiMethodId(46), Implemented, Description("Get Smart Fan Setting Mode")] void GetSmartFanSetting ([out, Description("Get Smart Setting Mode")] UINT32 Data); + [WmiMethodId(47), Implemented, Description("Get Power Charge Mode")] void GetPowerChargeMode ([out, Description("Get Power Charge Mode")] UINT32 Data); + [WmiMethodId(48), Implemented, Description("Get Gaming Product Info")] void GetProductInfo ([out, Description("Get Gaming Product Info")] UINT32 Data); + [WmiMethodId(49), Implemented, Description("Over Drive feature capability")] void IsSupportOD ([out, Description("Over Drive feature capability")] UINT32 Data); + [WmiMethodId(50), Implemented, Description("Get Over Drive status")] void GetODStatus ([out, Description("Get Over Drive status")] UINT32 Data); + [WmiMethodId(51), Implemented, Description("Set Over Drive status")] void SetODStatus ([in, Description("Set Over Drive status")] UINT32 Data); + [WmiMethodId(52), Implemented, Description("Set Light Control Owner")] void SetLightControlOwner ([in, Description("Set Light Control Owner")] UINT32 Data); + [WmiMethodId(53), Implemented, Description("Set DDS Control Owner")] void SetDDSControlOwner ([in, Description("Set DDS Control Owner")] UINT32 Data); + [WmiMethodId(54), Implemented, Description("Get the flag of restore OC value")] void IsRestoreOCValue ([in, Description("Clean this flag")] UINT32 idx, [out, Description("Restore oc value flag")] UINT32 Data); + [WmiMethodId(55), Implemented, Description("Get Real Thremal Mode")] void GetThermalMode ([out, Description("Real Thremal Mode")] UINT32 Data); + [WmiMethodId(56), Implemented, Description("Get the OC switch status in BIOS")] void GetBIOSOCMode ([out, Description("OC Mode")] UINT32 Data); + [WmiMethodId(59), Implemented, Description("Get hardware info support version")] void GetHardwareInfoSupportVersion ([out, Description("version")] UINT32 Data); + [WmiMethodId(60), Implemented, Description("Get Cpu core 0 max frequency")] void GetCpuFrequency ([out, Description("frequency")] UINT32 Data); + [WmiMethodId(62), Implemented, Description("Check the Adapter type fit for OC")] void IsACFitForOC ([out, Description("AC check result")] UINT32 Data); + [WmiMethodId(63), Implemented, Description("Is support IGPU mode")] void IsSupportIGPUMode ([out, Description("IGPU modes")] UINT32 Data); + [WmiMethodId(64), Implemented, Description("Get IGPU Mode Status")] void GetIGPUModeStatus([out, Description("IGPU Mode Status")] UINT32 Data); + [WmiMethodId(65), Implemented, Description("Set IGPU Mode")] void SetIGPUModeStatus([in, Description("IGPU Mode")] UINT32 mode, [out, Description("return code")] UINT32 Data); + [WmiMethodId(66), Implemented, Description("Notify DGPU Status")] void NotifyDGPUStatus([in, Description("DGPU status")] UINT32 status, [out, Description("return code")] UINT32 Data); + [WmiMethodId(67), Implemented, Description("Is changed Y log")] void IsChangedYLog([out, Description("Is changed Y Log")] UINT32 Data); + [WmiMethodId(68), Implemented, Description("Get DGPU Hardwawre ID")] void GetDGPUHWId([out, Description("Get DGPU Hardware ID")] string Data); + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of CPU OC parameter list"), guid("{B7F3CA0A-ACDC-42D2-9217-77C6C628FBD2}")] + class LENOVO_GAMEZONE_CPU_OC_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("OC tune id.")] uint32 Tuneid; + [WmiDataId(2), read, Description("Default value.")] uint32 DefaultValue; + [WmiDataId(3), read, Description("OC Value.")] uint32 OCValue; + [WmiDataId(4), read, Description("Min Value.")] uint32 MinValue; + [WmiDataId(5), read, Description("Max Value.")] uint32 MaxValue; + [WmiDataId(6), read, Description("Scale Value.")] uint32 ScaleValue; + [WmiDataId(7), read, Description("OC Order id.")] uint32 OCOrderid; + [WmiDataId(8), read, Description("NON-OC Order id.")] uint32 NOCOrderid; + [WmiDataId(9), read, Description("Delay time in ms.")] uint32 Interval; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Definition of GPU OC parameter list"), guid("{887B54E2-DDDC-4B2C-8B88-68A26A8835D0}")] + class LENOVO_GAMEZONE_GPU_OC_DATA { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("P-State ID.")] uint32 PStateID; + [WmiDataId(2), read, Description("CLOCK ID.")] uint32 ClockID; + [WmiDataId(3), read, Description("Default value.")] uint32 defaultvalue; + [WmiDataId(4), read, Description("OC Offset freqency.")] uint32 OCOffsetFreq; + [WmiDataId(5), read, Description("OC Min offset value.")] uint32 OCMinOffset; + [WmiDataId(6), read, Description("OC Max offset value.")] uint32 OCMaxOffset; + [WmiDataId(7), read, Description("OC Offset Scale.")] uint32 OCOffsetScale; + [WmiDataId(8), read, Description("OC Order id.")] uint32 OCOrderid; + [WmiDataId(9), read, Description("NON-OC Order id.")] uint32 NOCOrderid; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Fancooling finish event"), guid("{BC72A435-E8C1-4275-B3E2-D8B8074ABA59}")] + class LENOVO_GAMEZONE_FAN_COOLING_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Fancooling clean finish event")] uint32 EventId; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan mode change event"), guid("{D320289E-8FEA-41E0-86F9-611D83151B5F}")] + class LENOVO_GAMEZONE_SMART_FAN_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Smart Fan Mode change event")] uint32 mode; + [WmiDataId(2), read, Description("version of FN+Q")] uint32 version; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Smart Fan setting mode change event"), guid("{D320289E-8FEA-41E1-86F9-611D83151B5F}")] + class LENOVO_GAMEZONE_SMART_FAN_SETTING_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Smart Fan Setting mode change event")] uint32 mode; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("POWER CHARGE MODE Change EVENT"), guid("{D320289E-8FEA-41E0-86F9-711D83151B5F}")] + class LENOVO_GAMEZONE_POWER_CHARGE_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("POWER CHARGE MODE Change EVENT")] uint32 mode; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Thermal Mode Real Mode change event"), guid("{D320289E-8FEA-41E0-86F9-911D83151B5F}")] + class LENOVO_GAMEZONE_THERMAL_MODE_EVENT: WMIEvent { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description("Thermal Mode Real Mode")] uint32 mode; + }; diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst new file mode 100644 index 0000000000000..d7928b8dfb4b5 --- /dev/null +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -0,0 +1,108 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +=========================================================== +Lenovo WMI Interface Other Mode Driver (lenovo-wmi-other) +=========================================================== + +Introduction +============ +Lenovo WMI Other Mode interface is broken up into multiple GUIDs, +The primary Other Mode interface provides advanced power tuning features +such as Package Power Tracking (PPT). It is paired with multiple data block +GUIDs that provide context for the various methods. + + +Other Mode +---------- + +WMI GUID ``DC2A8805-3A8C-41BA-A6F7-092E0089CD3B`` + +The Other Mode WMI interface uses the firmware_attributes class to expose +various WMI attributes provided by the interface in the sysfs. This enables +CPU and GPU power limit tuning as well as various other attributes for +devices that fall under the "Gaming Series" of Lenovo devices. Each +attribute exposed by the Other Mode interface has corresponding +capability data blocks which allow the driver to probe details about the +attribute. Each attribute has multiple pages, one for each of the platform +profiles managed by the Gamezone interface. Attributes are exposed in sysfs +under the following path: + +:: + + /sys/class/firmware-attributes/lenovo-wmi-other/attributes// + +LENOVO_CAPABILITY_DATA_01 +------------------------- + +WMI GUID ``7A8F5407-CB67-4D6E-B547-39B3BE018154`` + +The LENOVO_CAPABILITY_DATA_01 interface provides information on various +power limits of integrated CPU and GPU components. + +Each attribute has the following properties: + - current_value + - default_value + - display_name + - max_value + - min_value + - scalar_increment + - type + +The following attributes are implemented: + - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit + - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking + - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking + + +WMI interface description +========================= + +The WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO_OTHER_METHOD class"), guid("{dc2a8805-3a8c-41ba-a6f7-092e0089cd3b}")] + class LENOVO_OTHER_METHOD { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(17), Implemented, Description("Get Feature Value ")] void GetFeatureValue([in] uint32 IDs, [out] uint32 value); + [WmiMethodId(18), Implemented, Description("Set Feature Value ")] void SetFeatureValue([in] uint32 IDs, [in] uint32 value); + [WmiMethodId(19), Implemented, Description("Get Data By Command ")] void GetDataByCommand([in] uint32 IDs, [in] uint32 Command, [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint32 Data[]); + [WmiMethodId(99), Implemented, Description("Get Data By Package for TAC")] void GetDataByPackage([in, Max(40)] uint8 Input[], [out] uint32 DataSize, [out, WmiSizeIs("DataSize")] uint8 Data[]); + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 00"), guid("{362a3afe-3d96-4665-8530-96dad5bb300e}")] + class LENOVO_CAPABILITY_DATA_00 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Capability Default Value.")] uint32 DefaultValue; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 01"), guid("{7a8f5407-cb67-4d6e-b547-39b3be018154}")] + class LENOVO_CAPABILITY_DATA_01 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Default Value.")] uint32 DefaultValue; + [WmiDataId(4), read, Description("Step.")] uint32 Step; + [WmiDataId(5), read, Description("Minimum Value.")] uint32 MinValue; + [WmiDataId(6), read, Description("Maximum Value.")] uint32 MaxValue; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("LENOVO CAPABILITY DATA 02"), guid("{bbf1f790-6c2f-422b-bc8c-4e7369c7f6ab}")] + class LENOVO_CAPABILITY_DATA_02 { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiDataId(1), read, Description(" IDs.")] uint32 IDs; + [WmiDataId(2), read, Description("Capability.")] uint32 Capability; + [WmiDataId(3), read, Description("Data Size.")] uint32 DataSize; + [WmiDataId(4), read, Description("Default Value"), WmiSizeIs("DataSize")] uint8 DefaultValue[]; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 2da44049e9e0d..5b67869bbc879 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13673,8 +13673,11 @@ F: drivers/usb/misc/legousbtower.c LENOVO drivers M: Mark Pearson +M: Derek J. Clark L: platform-driver-x86@vger.kernel.org S: Maintained +F: Documentation/wmi/devices/lenovo-wmi-gamezone.rst +F: Documentation/wmi/devices/lenovo-wmi-other.rst F: drivers/platform/x86/lenovo/* LENOVO WMI HOTKEY UTILITIES DRIVER -- GitLab From e521d16e76cd9ea99c585e064f4e7daf657b1451 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:22 -0700 Subject: [PATCH 379/868] platform/x86: Add lenovo-wmi-helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds lenovo-wmi-helpers, which provides a common wrapper function for wmidev_evaluate_method that does data validation and error handling. Reviewed-by: Alok Tiwari Reviewed-by: Armin Wolf Reviewed-by: Mario Limonciello Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-3-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 4 ++ drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/wmi-helpers.c | 74 +++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-helpers.h | 20 ++++++ 4 files changed, 99 insertions(+) create mode 100644 drivers/platform/x86/lenovo/wmi-helpers.c create mode 100644 drivers/platform/x86/lenovo/wmi-helpers.h diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 05dab29a22f71..abb4888898341 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -232,3 +232,7 @@ config YT2_1380 To compile this driver as a module, choose M here: the module will be called lenovo-yogabook. + +config LENOVO_WMI_HELPERS + tristate + depends on ACPI_WMI diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile index f1aa2449293b3..a347d657d2358 100644 --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -12,6 +12,7 @@ lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o +lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o # Add 'lenovo' prefix to each module listed in lenovo-target-* define LENOVO_OBJ_TARGET diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c new file mode 100644 index 0000000000000..f6fef6296251e --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-helpers.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo Legion WMI helpers driver. + * + * The Lenovo Legion WMI interface is broken up into multiple GUID interfaces + * that require cross-references between GUID's for some functionality. The + * "Custom Mode" interface is a legacy interface for managing and displaying + * CPU & GPU power and hwmon settings and readings. The "Other Mode" interface + * is a modern interface that replaces or extends the "Custom Mode" interface + * methods. The "Gamezone" interface adds advanced features such as fan + * profiles and overclocking. The "Lighting" interface adds control of various + * status lights related to different hardware components. Each of these + * drivers uses a common procedure to get data from the WMI interface, + * enumerated here. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include + +#include "wmi-helpers.h" + +/** + * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that + * return an integer. + * @wdev: Pointer to the WMI device to be called. + * @instance: Instance of the called method. + * @method_id: WMI Method ID for the method to be called. + * @buf: Buffer of all arguments for the given method_id. + * @size: Length of the buffer. + * @retval: Pointer for the return value to be assigned. + * + * Calls wmidev_evaluate_method for Lenovo WMI devices that return an ACPI + * integer. Validates the return value type and assigns the value to the + * retval pointer. + * + * Return: 0 on success, or an error code. + */ +int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, + unsigned char *buf, size_t size, u32 *retval) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *ret_obj __free(kfree) = NULL; + struct acpi_buffer input = { size, buf }; + acpi_status status; + + status = wmidev_evaluate_method(wdev, instance, method_id, &input, + &output); + if (ACPI_FAILURE(status)) + return -EIO; + + if (retval) { + ret_obj = output.pointer; + if (!ret_obj) + return -ENODATA; + + if (ret_obj->type != ACPI_TYPE_INTEGER) + return -ENXIO; + + *retval = (u32)ret_obj->integer.value; + } + + return 0; +}; +EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS"); + +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Lenovo WMI Helpers Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h new file mode 100644 index 0000000000000..20fd217498035 --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-helpers.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Copyright (C) 2025 Derek J. Clark */ + +#ifndef _LENOVO_WMI_HELPERS_H_ +#define _LENOVO_WMI_HELPERS_H_ + +#include + +struct wmi_device; + +struct wmi_method_args_32 { + u32 arg0; + u32 arg1; +}; + +int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id, + unsigned char *buf, size_t size, u32 *retval); + +#endif /* !_LENOVO_WMI_HELPERS_H_ */ -- GitLab From 949bf144bdc72e87018197ae71aa4959f17885d5 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:23 -0700 Subject: [PATCH 380/868] platform/x86: Add Lenovo WMI Events Driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds lenovo-wmi-events driver. The events driver is designed as a general entrypoint for all Lenovo WMI Events. It acts as a notification chain head that will process event data and pass it on to registered drivers so they can react to the events. Currently only the Gamezone interface Thermal Mode Event GUID is implemented in this driver. It is documented in the Gamezone documentation. Suggested-by: Armin Wolf Reviewed-by: Alok Tiwari Reviewed-by: Armin Wolf Reviewed-by: Mario Limonciello Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-4-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 4 + drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/wmi-events.c | 196 +++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-events.h | 20 +++ 4 files changed, 221 insertions(+) create mode 100644 drivers/platform/x86/lenovo/wmi-events.c create mode 100644 drivers/platform/x86/lenovo/wmi-events.h diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index abb4888898341..95dce00f257e8 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -233,6 +233,10 @@ config YT2_1380 To compile this driver as a module, choose M here: the module will be called lenovo-yogabook. +config LENOVO_WMI_EVENTS + tristate + depends on ACPI_WMI + config LENOVO_WMI_HELPERS tristate depends on ACPI_WMI diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile index a347d657d2358..814c4ba9bdbe3 100644 --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -12,6 +12,7 @@ lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o +lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o # Add 'lenovo' prefix to each module listed in lenovo-target-* diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c new file mode 100644 index 0000000000000..0994cd7dd504c --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-events.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo WMI Events driver. Lenovo WMI interfaces provide various + * hardware triggered events that many drivers need to have propagated. + * This driver provides a uniform entrypoint for these events so that + * any driver that needs to respond to these events can subscribe to a + * notifier chain. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include + +#include "wmi-events.h" +#include "wmi-gamezone.h" + +#define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F" + +#define LWMI_EVENT_DEVICE(guid, type) \ + .guid_string = (guid), .context = &(enum lwmi_events_type) \ + { \ + type \ + } + +static BLOCKING_NOTIFIER_HEAD(events_chain_head); + +struct lwmi_events_priv { + struct wmi_device *wdev; + enum lwmi_events_type type; +}; + +/** + * lwmi_events_register_notifier() - Add a notifier to the notifier chain. + * @nb: The notifier_block struct to register + * + * Call blocking_notifier_chain_register to register the notifier block to the + * lenovo-wmi-events driver blocking notifier chain. + * + * Return: 0 on success, %-EEXIST on error. + */ +int lwmi_events_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&events_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_events_register_notifier, "LENOVO_WMI_EVENTS"); + +/** + * lwmi_events_unregister_notifier() - Remove a notifier from the notifier + * chain. + * @nb: The notifier_block struct to unregister + * + * Call blocking_notifier_chain_unregister to unregister the notifier block + * from the lenovo-wmi-events driver blocking notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +int lwmi_events_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&events_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_events_unregister_notifier, "LENOVO_WMI_EVENTS"); + +/** + * devm_lwmi_events_unregister_notifier() - Remove a notifier from the notifier + * chain. + * @data: Void pointer to the notifier_block struct to unregister. + * + * Call lwmi_events_unregister_notifier to unregister the notifier block from + * the lenovo-wmi-events driver blocking notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +static void devm_lwmi_events_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + lwmi_events_unregister_notifier(nb); +} + +/** + * devm_lwmi_events_register_notifier() - Add a notifier to the notifier chain. + * @dev: The parent device of the notifier_block struct. + * @nb: The notifier_block struct to register + * + * Call lwmi_events_register_notifier to register the notifier block to the + * lenovo-wmi-events driver blocking notifier chain. Then add, as a device + * managed action, unregister_notifier to automatically unregister the + * notifier block upon its parent device removal. + * + * Return: 0 on success, or an error code. + */ +int devm_lwmi_events_register_notifier(struct device *dev, + struct notifier_block *nb) +{ + int ret; + + ret = lwmi_events_register_notifier(nb); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(dev, devm_lwmi_events_unregister_notifier, nb); +} +EXPORT_SYMBOL_NS_GPL(devm_lwmi_events_register_notifier, "LENOVO_WMI_EVENTS"); + +/** + * lwmi_events_notify() - Call functions for the notifier call chain. + * @wdev: The parent WMI device of the driver. + * @obj: ACPI object passed by the registered WMI Event. + * + * Validate WMI event data and notify all registered drivers of the event and + * its output. + * + * Return: 0 on success, or an error code. + */ +static void lwmi_events_notify(struct wmi_device *wdev, union acpi_object *obj) +{ + struct lwmi_events_priv *priv = dev_get_drvdata(&wdev->dev); + int sel_prof; + int ret; + + switch (priv->type) { + case LWMI_EVENT_THERMAL_MODE: + if (obj->type != ACPI_TYPE_INTEGER) + return; + + sel_prof = obj->integer.value; + + switch (sel_prof) { + case LWMI_GZ_THERMAL_MODE_QUIET: + case LWMI_GZ_THERMAL_MODE_BALANCED: + case LWMI_GZ_THERMAL_MODE_PERFORMANCE: + case LWMI_GZ_THERMAL_MODE_EXTREME: + case LWMI_GZ_THERMAL_MODE_CUSTOM: + ret = blocking_notifier_call_chain(&events_chain_head, + LWMI_EVENT_THERMAL_MODE, + &sel_prof); + if (ret == NOTIFY_BAD) + dev_err(&wdev->dev, + "Failed to send notification to call chain for WMI Events\n"); + return; + default: + dev_err(&wdev->dev, "Got invalid thermal mode: %x", + sel_prof); + return; + } + break; + default: + return; + } +} + +static int lwmi_events_probe(struct wmi_device *wdev, const void *context) +{ + struct lwmi_events_priv *priv; + + if (!context) + return -EINVAL; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + priv->type = *(enum lwmi_events_type *)context; + dev_set_drvdata(&wdev->dev, priv); + + return 0; +} + +static const struct wmi_device_id lwmi_events_id_table[] = { + { LWMI_EVENT_DEVICE(THERMAL_MODE_EVENT_GUID, LWMI_EVENT_THERMAL_MODE) }, + {} +}; + +static struct wmi_driver lwmi_events_driver = { + .driver = { + .name = "lenovo_wmi_events", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = lwmi_events_id_table, + .probe = lwmi_events_probe, + .notify = lwmi_events_notify, + .no_singleton = true, +}; + +module_wmi_driver(lwmi_events_driver); + +MODULE_DEVICE_TABLE(wmi, lwmi_events_id_table); +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Lenovo WMI Events Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-events.h b/drivers/platform/x86/lenovo/wmi-events.h new file mode 100644 index 0000000000000..cd34e886912ce --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-events.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Copyright (C) 2025 Derek J. Clark */ + +#ifndef _LENOVO_WMI_EVENTS_H_ +#define _LENOVO_WMI_EVENTS_H_ + +struct device; +struct notifier_block; + +enum lwmi_events_type { + LWMI_EVENT_THERMAL_MODE = 1, +}; + +int lwmi_events_register_notifier(struct notifier_block *nb); +int lwmi_events_unregister_notifier(struct notifier_block *nb); +int devm_lwmi_events_register_notifier(struct device *dev, + struct notifier_block *nb); + +#endif /* !_LENOVO_WMI_EVENTS_H_ */ -- GitLab From e1a5fe662b593108d14cd0481019601698f9fbe8 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:24 -0700 Subject: [PATCH 381/868] platform/x86: Add Lenovo Capability Data 01 WMI Driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds lenovo-wmi-capdata01 driver which provides the LENOVO_CAPABILITY_DATA_01 WMI data block that comes on "Other Mode" enabled hardware. Provides an interface for querying if a given attribute is supported by the hardware, as well as its default_value, max_value, min_value, and step increment. Reviewed-by: Alok Tiwari Reviewed-by: Armin Wolf Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-5-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 4 + drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/wmi-capdata01.c | 302 ++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-capdata01.h | 25 ++ 4 files changed, 332 insertions(+) create mode 100644 drivers/platform/x86/lenovo/wmi-capdata01.c create mode 100644 drivers/platform/x86/lenovo/wmi-capdata01.h diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 95dce00f257e8..1943e3a24f430 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -233,6 +233,10 @@ config YT2_1380 To compile this driver as a module, choose M here: the module will be called lenovo-yogabook. +config LENOVO_WMI_DATA01 + tristate + depends on ACPI_WMI + config LENOVO_WMI_EVENTS tristate depends on ACPI_WMI diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile index 814c4ba9bdbe3..1ce970e4ddb84 100644 --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -12,6 +12,7 @@ lenovo-target-$(CONFIG_LENOVO_YMC) += ymc.o lenovo-target-$(CONFIG_YOGABOOK) += yogabook.o lenovo-target-$(CONFIG_YT2_1380) += yoga-tab2-pro-1380-fastcharger.o lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o +lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o diff --git a/drivers/platform/x86/lenovo/wmi-capdata01.c b/drivers/platform/x86/lenovo/wmi-capdata01.c new file mode 100644 index 0000000000000..c922680b3cba3 --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-capdata01.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo Capability Data 01 WMI Data Block driver. + * + * Lenovo Capability Data 01 provides information on tunable attributes used by + * the "Other Mode" WMI interface. The data includes if the attribute is + * supported by the hardware, the default_value, max_value, min_value, and step + * increment. Each attribute has multiple pages, one for each of the thermal + * modes managed by the Gamezone interface. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wmi-capdata01.h" + +#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154" + +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_AC_NOTIFY_STATUS 0x80 + +struct lwmi_cd01_priv { + struct notifier_block acpi_nb; /* ACPI events */ + struct wmi_device *wdev; + struct cd01_list *list; +}; + +struct cd01_list { + struct mutex list_mutex; /* list R/W mutex */ + u8 count; + struct capdata01 data[]; +}; + +/** + * lwmi_cd01_component_bind() - Bind component to master device. + * @cd01_dev: Pointer to the lenovo-wmi-capdata01 driver parent device. + * @om_dev: Pointer to the lenovo-wmi-other driver parent device. + * @data: capdata01_list object pointer used to return the capability data. + * + * On lenovo-wmi-other's master bind, provide a pointer to the local capdata01 + * list. This is used to call lwmi_cd01_get_data to look up attribute data + * from the lenovo-wmi-other driver. + * + * Return: 0 + */ +static int lwmi_cd01_component_bind(struct device *cd01_dev, + struct device *om_dev, void *data) +{ + struct lwmi_cd01_priv *priv = dev_get_drvdata(cd01_dev); + struct cd01_list **cd01_list = data; + + *cd01_list = priv->list; + + return 0; +} + +static const struct component_ops lwmi_cd01_component_ops = { + .bind = lwmi_cd01_component_bind, +}; + +/** + * lwmi_cd01_get_data - Get the data of the specified attribute + * @list: The lenovo-wmi-capdata01 pointer to its cd01_list struct. + * @attribute_id: The capdata attribute ID to be found. + * @output: Pointer to a capdata01 struct to return the data. + * + * Retrieves the capability data 01 struct pointer for the given + * attribute for its specified thermal mode. + * + * Return: 0 on success, or -EINVAL. + */ +int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output) +{ + u8 idx; + + guard(mutex)(&list->list_mutex); + for (idx = 0; idx < list->count; idx++) { + if (list->data[idx].id != attribute_id) + continue; + memcpy(output, &list->data[idx], sizeof(list->data[idx])); + return 0; + }; + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CD01"); + +/** + * lwmi_cd01_cache() - Cache all WMI data block information + * @priv: lenovo-wmi-capdata01 driver data. + * + * Loop through each WMI data block and cache the data. + * + * Return: 0 on success, or an error. + */ +static int lwmi_cd01_cache(struct lwmi_cd01_priv *priv) +{ + int idx; + + guard(mutex)(&priv->list->list_mutex); + for (idx = 0; idx < priv->list->count; idx++) { + union acpi_object *ret_obj __free(kfree) = NULL; + + ret_obj = wmidev_block_query(priv->wdev, idx); + if (!ret_obj) + return -ENODEV; + + if (ret_obj->type != ACPI_TYPE_BUFFER || + ret_obj->buffer.length < sizeof(priv->list->data[idx])) + continue; + + memcpy(&priv->list->data[idx], ret_obj->buffer.pointer, + ret_obj->buffer.length); + } + + return 0; +} + +/** + * lwmi_cd01_alloc() - Allocate a cd01_list struct in drvdata + * @priv: lenovo-wmi-capdata01 driver data. + * + * Allocate a cd01_list struct large enough to contain data from all WMI data + * blocks provided by the interface. + * + * Return: 0 on success, or an error. + */ +static int lwmi_cd01_alloc(struct lwmi_cd01_priv *priv) +{ + struct cd01_list *list; + size_t list_size; + int count, ret; + + count = wmidev_instance_count(priv->wdev); + list_size = struct_size(list, data, count); + + list = devm_kzalloc(&priv->wdev->dev, list_size, GFP_KERNEL); + if (!list) + return -ENOMEM; + + ret = devm_mutex_init(&priv->wdev->dev, &list->list_mutex); + if (ret) + return ret; + + list->count = count; + priv->list = list; + + return 0; +} + +/** + * lwmi_cd01_setup() - Cache all WMI data block information + * @priv: lenovo-wmi-capdata01 driver data. + * + * Allocate a cd01_list struct large enough to contain data from all WMI data + * blocks provided by the interface. Then loop through each data block and + * cache the data. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_cd01_setup(struct lwmi_cd01_priv *priv) +{ + int ret; + + ret = lwmi_cd01_alloc(priv); + if (ret) + return ret; + + return lwmi_cd01_cache(priv); +} + +/** + * lwmi_cd01_notifier_call() - Call method for lenovo-wmi-capdata01 driver notifier. + * block call chain. + * @nb: The notifier_block registered to lenovo-wmi-events driver. + * @action: Unused. + * @data: The ACPI event. + * + * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile + * of a change. + * + * Return: notifier_block status. + */ +static int lwmi_cd01_notifier_call(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct acpi_bus_event *event = data; + struct lwmi_cd01_priv *priv; + int ret; + + if (strcmp(event->device_class, ACPI_AC_CLASS) != 0) + return NOTIFY_DONE; + + priv = container_of(nb, struct lwmi_cd01_priv, acpi_nb); + + switch (event->type) { + case ACPI_AC_NOTIFY_STATUS: + ret = lwmi_cd01_cache(priv); + if (ret) + return NOTIFY_BAD; + + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +/** + * lwmi_cd01_unregister() - Unregister the cd01 ACPI notifier_block. + * @data: The ACPI event notifier_block to unregister. + */ +static void lwmi_cd01_unregister(void *data) +{ + struct notifier_block *acpi_nb = data; + + unregister_acpi_notifier(acpi_nb); +} + +static int lwmi_cd01_probe(struct wmi_device *wdev, const void *context) + +{ + struct lwmi_cd01_priv *priv; + int ret; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + + ret = lwmi_cd01_setup(priv); + if (ret) + return ret; + + priv->acpi_nb.notifier_call = lwmi_cd01_notifier_call; + + ret = register_acpi_notifier(&priv->acpi_nb); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&wdev->dev, lwmi_cd01_unregister, &priv->acpi_nb); + if (ret) + return ret; + + return component_add(&wdev->dev, &lwmi_cd01_component_ops); +} + +static void lwmi_cd01_remove(struct wmi_device *wdev) +{ + component_del(&wdev->dev, &lwmi_cd01_component_ops); +} + +static const struct wmi_device_id lwmi_cd01_id_table[] = { + { LENOVO_CAPABILITY_DATA_01_GUID, NULL }, + {} +}; + +static struct wmi_driver lwmi_cd01_driver = { + .driver = { + .name = "lenovo_wmi_cd01", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = lwmi_cd01_id_table, + .probe = lwmi_cd01_probe, + .remove = lwmi_cd01_remove, + .no_singleton = true, +}; + +/** + * lwmi_cd01_match() - Match rule for the master driver. + * @dev: Pointer to the capability data 01 parent device. + * @data: Unused void pointer for passing match criteria. + * + * Return: int. + */ +int lwmi_cd01_match(struct device *dev, void *data) +{ + return dev->driver == &lwmi_cd01_driver.driver; +} +EXPORT_SYMBOL_NS_GPL(lwmi_cd01_match, "LENOVO_WMI_CD01"); + +module_wmi_driver(lwmi_cd01_driver); + +MODULE_DEVICE_TABLE(wmi, lwmi_cd01_id_table); +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Lenovo Capability Data 01 WMI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-capdata01.h b/drivers/platform/x86/lenovo/wmi-capdata01.h new file mode 100644 index 0000000000000..bd06c5751f68b --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-capdata01.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Copyright (C) 2025 Derek J. Clark */ + +#ifndef _LENOVO_WMI_CAPDATA01_H_ +#define _LENOVO_WMI_CAPDATA01_H_ + +#include + +struct device; +struct cd01_list; + +struct capdata01 { + u32 id; + u32 supported; + u32 default_value; + u32 step; + u32 min_value; + u32 max_value; +}; + +int lwmi_cd01_get_data(struct cd01_list *list, u32 attribute_id, struct capdata01 *output); +int lwmi_cd01_match(struct device *dev, void *data); + +#endif /* !_LENOVO_WMI_CAPDATA01_H_ */ -- GitLab From 22024ac5366f065a7b931bee5b62e2588521c4f0 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:25 -0700 Subject: [PATCH 382/868] platform/x86: Add Lenovo Gamezone WMI Driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds lenovo-wmi-gamezone driver which provides the Lenovo Gamezone WMI interface that comes on Lenovo "Gaming Series" hardware. Provides ACPI platform profiles over WMI. Reviewed-by: Armin Wolf Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-6-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 14 + drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/wmi-gamezone.c | 407 +++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-gamezone.h | 20 + 4 files changed, 442 insertions(+) create mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.c create mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 1943e3a24f430..9de050c637b7f 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -244,3 +244,17 @@ config LENOVO_WMI_EVENTS config LENOVO_WMI_HELPERS tristate depends on ACPI_WMI + +config LENOVO_WMI_GAMEZONE + tristate "Lenovo GameZone WMI Driver" + depends on ACPI_WMI + depends on DMI + select ACPI_PLATFORM_PROFILE + select LENOVO_WMI_EVENTS + select LENOVO_WMI_HELPERS + help + Say Y here if you have a WMI aware Lenovo Legion device and would like to use the + platform-profile firmware interface to manage power usage. + + To compile this driver as a module, choose M here: the module will + be called lenovo-wmi-gamezone. diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile index 1ce970e4ddb84..0722fd7ad4863 100644 --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -15,6 +15,7 @@ lenovo-target-$(CONFIG_LENOVO_WMI_CAMERA) += wmi-camera.o lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o +lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o # Add 'lenovo' prefix to each module listed in lenovo-target-* define LENOVO_OBJ_TARGET diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c new file mode 100644 index 0000000000000..0eb7fe8222f4a --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo GameZone WMI interface driver. + * + * The GameZone WMI interface provides platform profile and fan curve settings + * for devices that fall under the "Gaming Series" of Lenovo Legion devices. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wmi-events.h" +#include "wmi-gamezone.h" +#include "wmi-helpers.h" +#include "wmi-other.h" + +#define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0" + +#define LWMI_GZ_METHOD_ID_SMARTFAN_SUP 43 +#define LWMI_GZ_METHOD_ID_SMARTFAN_SET 44 +#define LWMI_GZ_METHOD_ID_SMARTFAN_GET 45 + +static BLOCKING_NOTIFIER_HEAD(gz_chain_head); + +struct lwmi_gz_priv { + enum thermal_mode current_mode; + struct notifier_block event_nb; + struct notifier_block mode_nb; + spinlock_t gz_mode_lock; /* current_mode lock */ + struct wmi_device *wdev; + int extreme_supported; + struct device *ppdev; +}; + +struct quirk_entry { + bool extreme_supported; +}; + +static struct quirk_entry quirk_no_extreme_bug = { + .extreme_supported = false, +}; + +/** + * lwmi_gz_mode_call() - Call method for lenovo-wmi-other driver notifier. + * + * @nb: The notifier_block registered to lenovo-wmi-other driver. + * @cmd: The event type. + * @data: Thermal mode enum pointer pointer for returning the thermal mode. + * + * For LWMI_GZ_GET_THERMAL_MODE, retrieve the current thermal mode. + * + * Return: Notifier_block status. + */ +static int lwmi_gz_mode_call(struct notifier_block *nb, unsigned long cmd, + void *data) +{ + enum thermal_mode **mode = data; + struct lwmi_gz_priv *priv; + + priv = container_of(nb, struct lwmi_gz_priv, mode_nb); + + switch (cmd) { + case LWMI_GZ_GET_THERMAL_MODE: + scoped_guard(spinlock, &priv->gz_mode_lock) { + **mode = priv->current_mode; + } + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +/** + * lwmi_gz_event_call() - Call method for lenovo-wmi-events driver notifier. + * block call chain. + * @nb: The notifier_block registered to lenovo-wmi-events driver. + * @cmd: The event type. + * @data: The data to be updated by the event. + * + * For LWMI_EVENT_THERMAL_MODE, set current_mode and notify platform_profile + * of a change. + * + * Return: notifier_block status. + */ +static int lwmi_gz_event_call(struct notifier_block *nb, unsigned long cmd, + void *data) +{ + enum thermal_mode *mode = data; + struct lwmi_gz_priv *priv; + + priv = container_of(nb, struct lwmi_gz_priv, event_nb); + + switch (cmd) { + case LWMI_EVENT_THERMAL_MODE: + scoped_guard(spinlock, &priv->gz_mode_lock) { + priv->current_mode = *mode; + } + platform_profile_notify(priv->ppdev); + return NOTIFY_STOP; + default: + return NOTIFY_DONE; + } +} + +/** + * lwmi_gz_thermal_mode_supported() - Get the version of the WMI + * interface to determine the support level. + * @wdev: The Gamezone WMI device. + * @supported: Pointer to return the support level with. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_gz_thermal_mode_supported(struct wmi_device *wdev, + int *supported) +{ + return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_SUP, + NULL, 0, supported); +} + +/** + * lwmi_gz_thermal_mode_get() - Get the current thermal mode. + * @wdev: The Gamezone interface WMI device. + * @mode: Pointer to return the thermal mode with. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_gz_thermal_mode_get(struct wmi_device *wdev, + enum thermal_mode *mode) +{ + return lwmi_dev_evaluate_int(wdev, 0x0, LWMI_GZ_METHOD_ID_SMARTFAN_GET, + NULL, 0, mode); +} + +/** + * lwmi_gz_profile_get() - Get the current platform profile. + * @dev: the Gamezone interface parent device. + * @profile: Pointer to provide the current platform profile with. + * + * Call lwmi_gz_thermal_mode_get and convert the thermal mode into a platform + * profile based on the support level of the interface. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_gz_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct lwmi_gz_priv *priv = dev_get_drvdata(dev); + enum thermal_mode mode; + int ret; + + ret = lwmi_gz_thermal_mode_get(priv->wdev, &mode); + if (ret) + return ret; + + switch (mode) { + case LWMI_GZ_THERMAL_MODE_QUIET: + *profile = PLATFORM_PROFILE_LOW_POWER; + break; + case LWMI_GZ_THERMAL_MODE_BALANCED: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case LWMI_GZ_THERMAL_MODE_PERFORMANCE: + if (priv->extreme_supported) { + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; + break; + } + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case LWMI_GZ_THERMAL_MODE_EXTREME: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case LWMI_GZ_THERMAL_MODE_CUSTOM: + *profile = PLATFORM_PROFILE_CUSTOM; + break; + default: + return -EINVAL; + } + + guard(spinlock)(&priv->gz_mode_lock); + priv->current_mode = mode; + + return 0; +} + +/** + * lwmi_gz_profile_set() - Set the current platform profile. + * @dev: The Gamezone interface parent device. + * @profile: Pointer to the desired platform profile. + * + * Convert the given platform profile into a thermal mode based on the support + * level of the interface, then call the WMI method to set the thermal mode. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_gz_profile_set(struct device *dev, + enum platform_profile_option profile) +{ + struct lwmi_gz_priv *priv = dev_get_drvdata(dev); + struct wmi_method_args_32 args; + enum thermal_mode mode; + int ret; + + switch (profile) { + case PLATFORM_PROFILE_LOW_POWER: + mode = LWMI_GZ_THERMAL_MODE_QUIET; + break; + case PLATFORM_PROFILE_BALANCED: + mode = LWMI_GZ_THERMAL_MODE_BALANCED; + break; + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: + mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; + break; + case PLATFORM_PROFILE_PERFORMANCE: + if (priv->extreme_supported) { + mode = LWMI_GZ_THERMAL_MODE_EXTREME; + break; + } + mode = LWMI_GZ_THERMAL_MODE_PERFORMANCE; + break; + case PLATFORM_PROFILE_CUSTOM: + mode = LWMI_GZ_THERMAL_MODE_CUSTOM; + break; + default: + return -EOPNOTSUPP; + } + + args.arg0 = mode; + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, + LWMI_GZ_METHOD_ID_SMARTFAN_SET, + (u8 *)&args, sizeof(args), NULL); + if (ret) + return ret; + + guard(spinlock)(&priv->gz_mode_lock); + priv->current_mode = mode; + + return 0; +} + +static const struct dmi_system_id fwbug_list[] = { + { + .ident = "Legion Go 8APU1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"), + }, + .driver_data = &quirk_no_extreme_bug, + }, + { + .ident = "Legion Go S 8APU1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8APU1"), + }, + .driver_data = &quirk_no_extreme_bug, + }, + { + .ident = "Legion Go S 8ARP1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8ARP1"), + }, + .driver_data = &quirk_no_extreme_bug, + }, + {}, + +}; + +/** + * lwmi_gz_extreme_supported() - Evaluate if a device supports extreme thermal mode. + * @profile_support_ver: Version of the WMI interface. + * + * Determine if the extreme thermal mode is supported by the hardware. + * Anything version 5 or lower does not. For devices with a version 6 or + * greater do a DMI check, as some devices report a version that supports + * extreme mode but have an incomplete entry in the BIOS. To ensure this + * cannot be set, quirk them to prevent assignment. + * + * Return: bool. + */ +static bool lwmi_gz_extreme_supported(int profile_support_ver) +{ + const struct dmi_system_id *dmi_id; + struct quirk_entry *quirks; + + if (profile_support_ver < 6) + return false; + + dmi_id = dmi_first_match(fwbug_list); + if (!dmi_id) + return true; + + quirks = dmi_id->driver_data; + + return quirks->extreme_supported; +} + +/** + * lwmi_gz_platform_profile_probe - Enable and set up the platform profile + * device. + * @drvdata: Driver data for the interface. + * @choices: Container for enabled platform profiles. + * + * Determine if thermal mode is supported, and if so to what feature level. + * Then enable all supported platform profiles. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_gz_platform_profile_probe(void *drvdata, unsigned long *choices) +{ + struct lwmi_gz_priv *priv = drvdata; + int profile_support_ver; + int ret; + + ret = lwmi_gz_thermal_mode_supported(priv->wdev, &profile_support_ver); + if (ret) + return ret; + + if (profile_support_ver < 1) + return -ENODEV; + + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + set_bit(PLATFORM_PROFILE_CUSTOM, choices); + + priv->extreme_supported = lwmi_gz_extreme_supported(profile_support_ver); + if (priv->extreme_supported) + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + + return 0; +} + +static const struct platform_profile_ops lwmi_gz_platform_profile_ops = { + .probe = lwmi_gz_platform_profile_probe, + .profile_get = lwmi_gz_profile_get, + .profile_set = lwmi_gz_profile_set, +}; + +static int lwmi_gz_probe(struct wmi_device *wdev, const void *context) +{ + struct lwmi_gz_priv *priv; + int ret; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + + priv->ppdev = devm_platform_profile_register(&wdev->dev, "lenovo-wmi-gamezone", + priv, &lwmi_gz_platform_profile_ops); + if (IS_ERR(priv->ppdev)) + return -ENODEV; + + spin_lock_init(&priv->gz_mode_lock); + + ret = lwmi_gz_thermal_mode_get(wdev, &priv->current_mode); + if (ret) + return ret; + + priv->event_nb.notifier_call = lwmi_gz_event_call; + ret = devm_lwmi_events_register_notifier(&wdev->dev, &priv->event_nb); + if (ret) + return ret; + + priv->mode_nb.notifier_call = lwmi_gz_mode_call; + return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb); +} + +static const struct wmi_device_id lwmi_gz_id_table[] = { + { LENOVO_GAMEZONE_GUID, NULL }, + {} +}; + +static struct wmi_driver lwmi_gz_driver = { + .driver = { + .name = "lenovo_wmi_gamezone", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = lwmi_gz_id_table, + .probe = lwmi_gz_probe, + .no_singleton = true, +}; + +module_wmi_driver(lwmi_gz_driver); + +MODULE_IMPORT_NS("LENOVO_WMI_EVENTS"); +MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); +MODULE_IMPORT_NS("LENOVO_WMI_OTHER"); +MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table); +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Lenovo GameZone WMI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h new file mode 100644 index 0000000000000..6b163a5eeb959 --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Copyright (C) 2025 Derek J. Clark */ + +#ifndef _LENOVO_WMI_GAMEZONE_H_ +#define _LENOVO_WMI_GAMEZONE_H_ + +enum gamezone_events_type { + LWMI_GZ_GET_THERMAL_MODE = 1, +}; + +enum thermal_mode { + LWMI_GZ_THERMAL_MODE_QUIET = 0x01, + LWMI_GZ_THERMAL_MODE_BALANCED = 0x02, + LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03, + LWMI_GZ_THERMAL_MODE_EXTREME = 0xE0, /* Ver 6+ */ + LWMI_GZ_THERMAL_MODE_CUSTOM = 0xFF, +}; + +#endif /* !_LENOVO_WMI_GAMEZONE_H_ */ -- GitLab From edc4b183b794baefb54aa0baeb810fe3ac65d826 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Tue, 1 Jul 2025 20:38:26 -0700 Subject: [PATCH 383/868] platform/x86: Add Lenovo Other Mode WMI Driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds lenovo-wmi-other driver which provides the Lenovo "Other Mode" WMI interface that comes on some Lenovo "Gaming Series" hardware. Provides a firmware-attributes class which enables the use of tunable knobs for SPL, SPPT, and FPPT. Reviewed-by: Alok Tiwari Reviewed-by: Armin Wolf Signed-off-by: Derek J. Clark Link: https://lore.kernel.org/r/20250702033826.1057762-7-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 15 + drivers/platform/x86/lenovo/Makefile | 1 + drivers/platform/x86/lenovo/wmi-other.c | 665 ++++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-other.h | 16 + 4 files changed, 697 insertions(+) create mode 100644 drivers/platform/x86/lenovo/wmi-other.c create mode 100644 drivers/platform/x86/lenovo/wmi-other.h diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 9de050c637b7f..b76157b35296a 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -258,3 +258,18 @@ config LENOVO_WMI_GAMEZONE To compile this driver as a module, choose M here: the module will be called lenovo-wmi-gamezone. + +config LENOVO_WMI_TUNING + tristate "Lenovo Other Mode WMI Driver" + depends on ACPI_WMI + select FW_ATTR_CLASS + select LENOVO_WMI_DATA01 + select LENOVO_WMI_EVENTS + select LENOVO_WMI_HELPERS + help + Say Y here if you have a WMI aware Lenovo Legion device and would like to use the + firmware_attributes API to control various tunable settings typically exposed by + Lenovo software in Windows. + + To compile this driver as a module, choose M here: the module will + be called lenovo-wmi-other. diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile index 0722fd7ad4863..7b2128e3a2142 100644 --- a/drivers/platform/x86/lenovo/Makefile +++ b/drivers/platform/x86/lenovo/Makefile @@ -16,6 +16,7 @@ lenovo-target-$(CONFIG_LENOVO_WMI_DATA01) += wmi-capdata01.o lenovo-target-$(CONFIG_LENOVO_WMI_EVENTS) += wmi-events.o lenovo-target-$(CONFIG_LENOVO_WMI_HELPERS) += wmi-helpers.o lenovo-target-$(CONFIG_LENOVO_WMI_GAMEZONE) += wmi-gamezone.o +lenovo-target-$(CONFIG_LENOVO_WMI_TUNING) += wmi-other.o # Add 'lenovo' prefix to each module listed in lenovo-target-* define LENOVO_OBJ_TARGET diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c new file mode 100644 index 0000000000000..2a960b278f117 --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Lenovo Other Mode WMI interface driver. + * + * This driver uses the fw_attributes class to expose the various WMI functions + * provided by the "Other Mode" WMI interface. This enables CPU and GPU power + * limit as well as various other attributes for devices that fall under the + * "Gaming Series" of Lenovo laptop devices. Each attribute exposed by the + * "Other Mode" interface has a corresponding Capability Data struct that + * allows the driver to probe details about the attribute such as if it is + * supported by the hardware, the default_value, max_value, min_value, and step + * increment. + * + * These attributes typically don't fit anywhere else in the sysfs and are set + * in Windows using one of Lenovo's multiple user applications. + * + * Copyright (C) 2025 Derek J. Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wmi-capdata01.h" +#include "wmi-events.h" +#include "wmi-gamezone.h" +#include "wmi-helpers.h" +#include "wmi-other.h" +#include "../firmware_attributes_class.h" + +#define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" + +#define LWMI_DEVICE_ID_CPU 0x01 + +#define LWMI_FEATURE_ID_CPU_SPPT 0x01 +#define LWMI_FEATURE_ID_CPU_SPL 0x02 +#define LWMI_FEATURE_ID_CPU_FPPT 0x03 + +#define LWMI_TYPE_ID_NONE 0x00 + +#define LWMI_FEATURE_VALUE_GET 17 +#define LWMI_FEATURE_VALUE_SET 18 + +#define LWMI_ATTR_DEV_ID_MASK GENMASK(31, 24) +#define LWMI_ATTR_FEAT_ID_MASK GENMASK(23, 16) +#define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8) +#define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0) + +#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" + +static BLOCKING_NOTIFIER_HEAD(om_chain_head); +static DEFINE_IDA(lwmi_om_ida); + +enum attribute_property { + DEFAULT_VAL, + MAX_VAL, + MIN_VAL, + STEP_VAL, + SUPPORTED, +}; + +struct lwmi_om_priv { + struct component_master_ops *ops; + struct cd01_list *cd01_list; /* only valid after capdata01 bind */ + struct device *fw_attr_dev; + struct kset *fw_attr_kset; + struct notifier_block nb; + struct wmi_device *wdev; + int ida_id; +}; + +struct tunable_attr_01 { + struct capdata01 *capdata; + struct device *dev; + u32 feature_id; + u32 device_id; + u32 type_id; +}; + +static struct tunable_attr_01 ppt_pl1_spl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl2_sppt = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPPT, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl3_fppt = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_FPPT, + .type_id = LWMI_TYPE_ID_NONE, +}; + +struct capdata01_attr_group { + const struct attribute_group *attr_group; + struct tunable_attr_01 *tunable_attr; +}; + +/** + * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain + * @nb: The notifier_block struct to register + * + * Call blocking_notifier_chain_register to register the notifier block to the + * lenovo-wmi-other driver notifier chain. + * + * Return: 0 on success, %-EEXIST on error. + */ +int lwmi_om_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&om_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); + +/** + * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier + * chain. + * @nb: The notifier_block struct to register + * + * Call blocking_notifier_chain_unregister to unregister the notifier block from the + * lenovo-wmi-other driver notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +int lwmi_om_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&om_chain_head, nb); +} +EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER"); + +/** + * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking + * notifier chain. + * @data: Void pointer to the notifier_block struct to register. + * + * Call lwmi_om_unregister_notifier to unregister the notifier block from the + * lenovo-wmi-other driver notifier chain. + * + * Return: 0 on success, %-ENOENT on error. + */ +static void devm_lwmi_om_unregister_notifier(void *data) +{ + struct notifier_block *nb = data; + + lwmi_om_unregister_notifier(nb); +} + +/** + * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier + * chain. + * @dev: The parent device of the notifier_block struct. + * @nb: The notifier_block struct to register + * + * Call lwmi_om_register_notifier to register the notifier block to the + * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier + * as a device managed action to automatically unregister the notifier block + * upon parent device removal. + * + * Return: 0 on success, or an error code. + */ +int devm_lwmi_om_register_notifier(struct device *dev, + struct notifier_block *nb) +{ + int ret; + + ret = lwmi_om_register_notifier(nb); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier, + nb); +} +EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER"); + +/** + * lwmi_om_notifier_call() - Call functions for the notifier call chain. + * @mode: Pointer to a thermal mode enum to retrieve the data from. + * + * Call blocking_notifier_call_chain to retrieve the thermal mode from the + * lenovo-wmi-gamezone driver. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_om_notifier_call(enum thermal_mode *mode) +{ + int ret; + + ret = blocking_notifier_call_chain(&om_chain_head, + LWMI_GZ_GET_THERMAL_MODE, &mode); + if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK) + return -EINVAL; + + return 0; +} + +/* Attribute Methods */ + +/** + * int_type_show() - Emit the data type for an integer attribute + * @kobj: Pointer to the driver object. + * @kattr: Pointer to the attribute calling this function. + * @buf: The buffer to write to. + * + * Return: Number of characters written to buf. + */ +static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *kattr, + char *buf) +{ + return sysfs_emit(buf, "integer\n"); +} + +/** + * attr_capdata01_show() - Get the value of the specified attribute property + * + * @kobj: Pointer to the driver object. + * @kattr: Pointer to the attribute calling this function. + * @buf: The buffer to write to. + * @tunable_attr: The attribute to be read. + * @prop: The property of this attribute to be read. + * + * Retrieves the given property from the capability data 01 struct for the + * specified attribute's "custom" thermal mode. This function is intended + * to be generic so it can be called from any integer attributes "_show" + * function. + * + * If the WMI is success the sysfs attribute is notified. + * + * Return: Either number of characters written to buf, or an error code. + */ +static ssize_t attr_capdata01_show(struct kobject *kobj, + struct kobj_attribute *kattr, char *buf, + struct tunable_attr_01 *tunable_attr, + enum attribute_property prop) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); + struct capdata01 capdata; + u32 attribute_id; + int value, ret; + + attribute_id = + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, + LWMI_GZ_THERMAL_MODE_CUSTOM) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + + ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); + if (ret) + return ret; + + switch (prop) { + case DEFAULT_VAL: + value = capdata.default_value; + break; + case MAX_VAL: + value = capdata.max_value; + break; + case MIN_VAL: + value = capdata.min_value; + break; + case STEP_VAL: + value = capdata.step; + break; + default: + return -EINVAL; + } + + return sysfs_emit(buf, "%d\n", value); +} + +/** + * attr_current_value_store() - Set the current value of the given attribute + * @kobj: Pointer to the driver object. + * @kattr: Pointer to the attribute calling this function. + * @buf: The buffer to read from, this is parsed to `int` type. + * @count: Required by sysfs attribute macros, pass in from the callee attr. + * @tunable_attr: The attribute to be stored. + * + * Sets the value of the given attribute when operating under the "custom" + * smartfan profile. The current smartfan profile is retrieved from the + * lenovo-wmi-gamezone driver and error is returned if the result is not + * "custom". This function is intended to be generic so it can be called from + * any integer attribute's "_store" function. The integer to be sent to the WMI + * method is range checked and an error code is returned if out of range. + * + * If the value is valid and WMI is success, then the sysfs attribute is + * notified. + * + * Return: Either count, or an error code. + */ +static ssize_t attr_current_value_store(struct kobject *kobj, + struct kobj_attribute *kattr, + const char *buf, size_t count, + struct tunable_attr_01 *tunable_attr) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); + struct wmi_method_args_32 args; + struct capdata01 capdata; + enum thermal_mode mode; + u32 attribute_id; + u32 value; + int ret; + + ret = lwmi_om_notifier_call(&mode); + if (ret) + return ret; + + if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM) + return -EBUSY; + + attribute_id = + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + + ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); + if (ret) + return ret; + + ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value < capdata.min_value || value > capdata.max_value) + return -EINVAL; + + args.arg0 = attribute_id; + args.arg1 = value; + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, + (unsigned char *)&args, sizeof(args), NULL); + if (ret) + return ret; + + return count; +}; + +/** + * attr_current_value_show() - Get the current value of the given attribute + * @kobj: Pointer to the driver object. + * @kattr: Pointer to the attribute calling this function. + * @buf: The buffer to write to. + * @tunable_attr: The attribute to be read. + * + * Retrieves the value of the given attribute for the current smartfan profile. + * The current smartfan profile is retrieved from the lenovo-wmi-gamezone driver. + * This function is intended to be generic so it can be called from any integer + * attribute's "_show" function. + * + * If the WMI is success the sysfs attribute is notified. + * + * Return: Either number of characters written to buf, or an error code. + */ +static ssize_t attr_current_value_show(struct kobject *kobj, + struct kobj_attribute *kattr, char *buf, + struct tunable_attr_01 *tunable_attr) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); + struct wmi_method_args_32 args; + enum thermal_mode mode; + u32 attribute_id; + int retval; + int ret; + + ret = lwmi_om_notifier_call(&mode); + if (ret) + return ret; + + attribute_id = + FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) | + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) | + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) | + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id); + + args.arg0 = attribute_id; + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, + (unsigned char *)&args, sizeof(args), + &retval); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", retval); +} + +/* Lenovo WMI Other Mode Attribute macros */ +#define __LWMI_ATTR_RO(_func, _name) \ + { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = _func##_##_name##_show, \ + } + +#define __LWMI_ATTR_RO_AS(_name, _show) \ + { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = _show, \ + } + +#define __LWMI_ATTR_RW(_func, _name) \ + __ATTR(_name, 0644, _func##_##_name##_show, _func##_##_name##_store) + +/* Shows a formatted static variable */ +#define __LWMI_ATTR_SHOW_FMT(_prop, _attrname, _fmt, _val) \ + static ssize_t _attrname##_##_prop##_show( \ + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ + { \ + return sysfs_emit(buf, _fmt, _val); \ + } \ + static struct kobj_attribute attr_##_attrname##_##_prop = \ + __LWMI_ATTR_RO(_attrname, _prop) + +/* Attribute current value read/write */ +#define __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname) \ + static ssize_t _attrname##_current_value_store( \ + struct kobject *kobj, struct kobj_attribute *kattr, \ + const char *buf, size_t count) \ + { \ + return attr_current_value_store(kobj, kattr, buf, count, \ + &_attrname); \ + } \ + static ssize_t _attrname##_current_value_show( \ + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ + { \ + return attr_current_value_show(kobj, kattr, buf, &_attrname); \ + } \ + static struct kobj_attribute attr_##_attrname##_current_value = \ + __LWMI_ATTR_RW(_attrname, current_value) + +/* Attribute property read only */ +#define __LWMI_TUNABLE_RO_CAP01(_prop, _attrname, _prop_type) \ + static ssize_t _attrname##_##_prop##_show( \ + struct kobject *kobj, struct kobj_attribute *kattr, char *buf) \ + { \ + return attr_capdata01_show(kobj, kattr, buf, &_attrname, \ + _prop_type); \ + } \ + static struct kobj_attribute attr_##_attrname##_##_prop = \ + __LWMI_ATTR_RO(_attrname, _prop) + +#define LWMI_ATTR_GROUP_TUNABLE_CAP01(_attrname, _fsname, _dispname) \ + __LWMI_TUNABLE_CURRENT_VALUE_CAP01(_attrname); \ + __LWMI_TUNABLE_RO_CAP01(default_value, _attrname, DEFAULT_VAL); \ + __LWMI_ATTR_SHOW_FMT(display_name, _attrname, "%s\n", _dispname); \ + __LWMI_TUNABLE_RO_CAP01(max_value, _attrname, MAX_VAL); \ + __LWMI_TUNABLE_RO_CAP01(min_value, _attrname, MIN_VAL); \ + __LWMI_TUNABLE_RO_CAP01(scalar_increment, _attrname, STEP_VAL); \ + static struct kobj_attribute attr_##_attrname##_type = \ + __LWMI_ATTR_RO_AS(type, int_type_show); \ + static struct attribute *_attrname##_attrs[] = { \ + &attr_##_attrname##_current_value.attr, \ + &attr_##_attrname##_default_value.attr, \ + &attr_##_attrname##_display_name.attr, \ + &attr_##_attrname##_max_value.attr, \ + &attr_##_attrname##_min_value.attr, \ + &attr_##_attrname##_scalar_increment.attr, \ + &attr_##_attrname##_type.attr, \ + NULL, \ + }; \ + static const struct attribute_group _attrname##_attr_group = { \ + .name = _fsname, .attrs = _attrname##_attrs \ + } + +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl", + "Set the CPU sustained power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt", + "Set the CPU slow package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt", + "Set the CPU fast package power tracking limit"); + +static struct capdata01_attr_group cd01_attr_groups[] = { + { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, + { &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt }, + { &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt }, + {}, +}; + +/** + * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members + * @priv: The Other Mode driver data. + * + * Return: Either 0, or an error code. + */ +static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) +{ + unsigned int i; + int err; + + priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL); + if (priv->ida_id < 0) + return priv->ida_id; + + priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, + MKDEV(0, 0), NULL, "%s-%u", + LWMI_OM_FW_ATTR_BASE_PATH, + priv->ida_id); + if (IS_ERR(priv->fw_attr_dev)) { + err = PTR_ERR(priv->fw_attr_dev); + goto err_free_ida; + } + + priv->fw_attr_kset = kset_create_and_add("attributes", NULL, + &priv->fw_attr_dev->kobj); + if (!priv->fw_attr_kset) { + err = -ENOMEM; + goto err_destroy_classdev; + } + + for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) { + err = sysfs_create_group(&priv->fw_attr_kset->kobj, + cd01_attr_groups[i].attr_group); + if (err) + goto err_remove_groups; + + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev; + } + return 0; + +err_remove_groups: + while (i--) + sysfs_remove_group(&priv->fw_attr_kset->kobj, + cd01_attr_groups[i].attr_group); + + kset_unregister(priv->fw_attr_kset); + +err_destroy_classdev: + device_unregister(priv->fw_attr_dev); + +err_free_ida: + ida_free(&lwmi_om_ida, priv->ida_id); + return err; +} + +/** + * lwmi_om_fw_attr_remove() - Unregister all capability data attribute groups + * @priv: the lenovo-wmi-other driver data. + */ +static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) + sysfs_remove_group(&priv->fw_attr_kset->kobj, + cd01_attr_groups[i].attr_group); + + kset_unregister(priv->fw_attr_kset); + device_unregister(priv->fw_attr_dev); +} + +/** + * lwmi_om_master_bind() - Bind all components of the other mode driver + * @dev: The lenovo-wmi-other driver basic device. + * + * Call component_bind_all to bind the lenovo-wmi-capdata01 driver to the + * lenovo-wmi-other master driver. On success, assign the capability data 01 + * list pointer to the driver data struct for later access. This pointer + * is only valid while the capdata01 interface exists. Finally, register all + * firmware attribute groups. + * + * Return: 0 on success, or an error code. + */ +static int lwmi_om_master_bind(struct device *dev) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(dev); + struct cd01_list *tmp_list; + int ret; + + ret = component_bind_all(dev, &tmp_list); + if (ret) + return ret; + + priv->cd01_list = tmp_list; + if (!priv->cd01_list) + return -ENODEV; + + return lwmi_om_fw_attr_add(priv); +} + +/** + * lwmi_om_master_unbind() - Unbind all components of the other mode driver + * @dev: The lenovo-wmi-other driver basic device + * + * Unregister all capability data attribute groups. Then call + * component_unbind_all to unbind the lenovo-wmi-capdata01 driver from the + * lenovo-wmi-other master driver. Finally, free the IDA for this device. + */ +static void lwmi_om_master_unbind(struct device *dev) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(dev); + + lwmi_om_fw_attr_remove(priv); + component_unbind_all(dev, NULL); +} + +static const struct component_master_ops lwmi_om_master_ops = { + .bind = lwmi_om_master_bind, + .unbind = lwmi_om_master_unbind, +}; + +static int lwmi_other_probe(struct wmi_device *wdev, const void *context) +{ + struct component_match *master_match = NULL; + struct lwmi_om_priv *priv; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + + component_match_add(&wdev->dev, &master_match, lwmi_cd01_match, NULL); + if (IS_ERR(master_match)) + return PTR_ERR(master_match); + + return component_master_add_with_match(&wdev->dev, &lwmi_om_master_ops, + master_match); +} + +static void lwmi_other_remove(struct wmi_device *wdev) +{ + struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev); + + component_master_del(&wdev->dev, &lwmi_om_master_ops); + ida_free(&lwmi_om_ida, priv->ida_id); +} + +static const struct wmi_device_id lwmi_other_id_table[] = { + { LENOVO_OTHER_MODE_GUID, NULL }, + {} +}; + +static struct wmi_driver lwmi_other_driver = { + .driver = { + .name = "lenovo_wmi_other", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = lwmi_other_id_table, + .probe = lwmi_other_probe, + .remove = lwmi_other_remove, + .no_singleton = true, +}; + +module_wmi_driver(lwmi_other_driver); + +MODULE_IMPORT_NS("LENOVO_WMI_CD01"); +MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); +MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table); +MODULE_AUTHOR("Derek J. Clark "); +MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-other.h b/drivers/platform/x86/lenovo/wmi-other.h new file mode 100644 index 0000000000000..8ebf5602bb997 --- /dev/null +++ b/drivers/platform/x86/lenovo/wmi-other.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Copyright (C) 2025 Derek J. Clark */ + +#ifndef _LENOVO_WMI_OTHER_H_ +#define _LENOVO_WMI_OTHER_H_ + +struct device; +struct notifier_block; + +int lwmi_om_register_notifier(struct notifier_block *nb); +int lwmi_om_unregister_notifier(struct notifier_block *nb); +int devm_lwmi_om_register_notifier(struct device *dev, + struct notifier_block *nb); + +#endif /* !_LENOVO_WMI_OTHER_H_ */ -- GitLab From fb1311b3f171bbb3c07cc7764ec981605564c83a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:16 -0700 Subject: [PATCH 384/868] MAINTAINERS: Add link to documentation of Intel PMT ABI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a link to the documentation for the Intel Platform Monitoring Technology ABI in Documentation/ABI/testing/sysfs-class-intel_pmt Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-2-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b67869bbc879..4af5a8ee40579 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12386,6 +12386,7 @@ F: include/linux/mfd/intel_soc_pmic* INTEL PMT DRIVERS M: David E. Box S: Supported +F: Documentation/ABI/testing/sysfs-class-intel_pmt F: drivers/platform/x86/intel/pmt/ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT -- GitLab From dc957ab6aa05c118c3da0542428a4d6602aa2d2d Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:17 -0700 Subject: [PATCH 385/868] platform/x86/intel/vsec: Add private data for per-device data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new private structure, struct vsec_priv, to hold a pointer to the platform-specific information. Although the driver didn’t previously require this per-device data, adding it now lays the groundwork for upcoming patches that will manage such data. No functional changes. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-3-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 055ca9f48fb49..59fb6568a855a 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -32,6 +32,10 @@ static DEFINE_IDA(intel_vsec_ida); static DEFINE_IDA(intel_vsec_sdsi_ida); static DEFINE_XARRAY_ALLOC(auxdev_array); +struct vsec_priv { + struct intel_vsec_platform_info *info; +}; + static const char *intel_vsec_name(enum intel_vsec_id id) { switch (id) { @@ -348,6 +352,7 @@ EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct intel_vsec_platform_info *info; + struct vsec_priv *priv; bool have_devices = false; int ret; @@ -360,6 +365,13 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id if (!info) return -EINVAL; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + pci_set_drvdata(pdev, priv); + if (intel_vsec_walk_dvsec(pdev, info)) have_devices = true; -- GitLab From b0631f8a5740c55b52d02174cc4c9c84cc7a16a1 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:18 -0700 Subject: [PATCH 386/868] platform/x86/intel/vsec: Create wrapper to walk PCI config space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combine three PCI config space walkers — intel_vsec_walk_dvsec(), intel_vsec_walk_vsec(), and intel_vsec_walk_header() — into a new wrapper function, intel_vsec_feature_walk(). This refactoring simplifies the probe logic and lays the groundwork for future patches that will loop over these calls. No functional changes. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-4-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 38 +++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 59fb6568a855a..8bdb74d86f240 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -349,11 +349,35 @@ int intel_vsec_register(struct pci_dev *pdev, } EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); +static bool intel_vsec_get_features(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) +{ + bool found = false; + + /* + * Both DVSEC and VSEC capabilities can exist on the same device, + * so both intel_vsec_walk_dvsec() and intel_vsec_walk_vsec() must be + * called independently. Additionally, intel_vsec_walk_header() is + * needed for devices that do not have VSEC/DVSEC but provide the + * information via device_data. + */ + if (intel_vsec_walk_dvsec(pdev, info)) + found = true; + + if (intel_vsec_walk_vsec(pdev, info)) + found = true; + + if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && + intel_vsec_walk_header(pdev, info)) + found = true; + + return found; +} + static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct intel_vsec_platform_info *info; struct vsec_priv *priv; - bool have_devices = false; int ret; ret = pcim_enable_device(pdev); @@ -372,17 +396,7 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id priv->info = info; pci_set_drvdata(pdev, priv); - if (intel_vsec_walk_dvsec(pdev, info)) - have_devices = true; - - if (intel_vsec_walk_vsec(pdev, info)) - have_devices = true; - - if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, info)) - have_devices = true; - - if (!have_devices) + if (!intel_vsec_get_features(pdev, info)) return -ENODEV; return 0; -- GitLab From 8a67d4b49bbdebcd255abde9e652092c3de3b657 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:19 -0700 Subject: [PATCH 387/868] platform/x86/intel/vsec: Add device links to enforce dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Intel VSEC features will have dependencies on other features, requiring certain supplier drivers to be probed before their consumers. To enforce this dependency ordering, introduce device links using device_link_add(), ensuring that suppliers are fully registered before consumers are probed. - Add device link tracking by storing supplier devices and tracking their state. - Implement intel_vsec_link_devices() to establish links between suppliers and consumers based on feature dependencies. - Add get_consumer_dependencies() to retrieve supplier-consumer relationships. - Modify feature registration logic: * Consumers now check that all required suppliers are registered before being initialized. * suppliers_ready() verifies that all required supplier devices are available. - Prevent potential null consumer name issue in sysfs: - Use dev_set_name() when creating auxiliary devices to ensure a unique, non-null consumer name. - Update intel_vsec_pci_probe() to loop up to the number of possible features or when all devices are registered, whichever comes first. - Introduce VSEC_CAP_UNUSED to prevent sub-features (registered via exported APIs) from being mistakenly linked. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-5-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 223 ++++++++++++++++++++++++++++-- include/linux/intel_vsec.h | 28 +++- 2 files changed, 236 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 8bdb74d86f240..aa1f7e63039d0 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -15,9 +15,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -32,8 +35,17 @@ static DEFINE_IDA(intel_vsec_ida); static DEFINE_IDA(intel_vsec_sdsi_ida); static DEFINE_XARRAY_ALLOC(auxdev_array); +enum vsec_device_state { + STATE_NOT_FOUND, + STATE_REGISTERED, + STATE_SKIP, +}; + struct vsec_priv { struct intel_vsec_platform_info *info; + struct device *suppliers[VSEC_FEATURE_COUNT]; + enum vsec_device_state state[VSEC_FEATURE_COUNT]; + unsigned long found_caps; }; static const char *intel_vsec_name(enum intel_vsec_id id) @@ -95,6 +107,74 @@ static void intel_vsec_dev_release(struct device *dev) kfree(intel_vsec_dev); } +static const struct vsec_feature_dependency * +get_consumer_dependencies(struct vsec_priv *priv, int cap_id) +{ + const struct vsec_feature_dependency *deps = priv->info->deps; + int consumer_id = priv->info->num_deps; + + if (!deps) + return NULL; + + while (consumer_id--) + if (deps[consumer_id].feature == BIT(cap_id)) + return &deps[consumer_id]; + + return NULL; +} + +/* + * Although pci_device_id table is available in the pdev, this prototype is + * necessary because the code using it can be called by an exported API that + * might pass a different pdev. + */ +static const struct pci_device_id intel_vsec_pci_ids[]; + +static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, + int consumer_id) +{ + const struct vsec_feature_dependency *deps; + enum vsec_device_state *state; + struct device **suppliers; + struct vsec_priv *priv; + int supplier_id; + + if (!consumer_id) + return 0; + + if (!pci_match_id(intel_vsec_pci_ids, pdev)) + return 0; + + priv = pci_get_drvdata(pdev); + state = priv->state; + suppliers = priv->suppliers; + + priv->suppliers[consumer_id] = dev; + + deps = get_consumer_dependencies(priv, consumer_id); + if (!deps) + return 0; + + for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) { + struct device_link *link; + + if (state[supplier_id] != STATE_REGISTERED) + continue; + + if (!suppliers[supplier_id]) { + dev_err(dev, "Bad supplier list\n"); + return -EINVAL; + } + + link = device_link_add(dev, suppliers[supplier_id], + DL_FLAG_AUTOPROBE_CONSUMER); + if (!link) + return -EINVAL; + } + + return 0; +} + int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name) @@ -132,19 +212,37 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, return ret; } + /* + * Assign a name now to ensure that the device link doesn't contain + * a null string for the consumer name. This is a problem when a supplier + * supplies more than one consumer and can lead to a duplicate name error + * when the link is created in sysfs. + */ + ret = dev_set_name(&auxdev->dev, "%s.%s.%d", KBUILD_MODNAME, auxdev->name, + auxdev->id); + if (ret) + goto cleanup_aux; + + ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); + if (ret) + goto cleanup_aux; + ret = auxiliary_device_add(auxdev); - if (ret < 0) { - auxiliary_device_uninit(auxdev); - return ret; - } + if (ret) + goto cleanup_aux; return devm_add_action_or_reset(parent, intel_vsec_remove_aux, auxdev); + +cleanup_aux: + auxiliary_device_uninit(auxdev); + return ret; } EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info) + struct intel_vsec_platform_info *info, + unsigned long cap_id) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; struct resource __free(kfree) *res = NULL; @@ -211,6 +309,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->priv_data = info->priv_data; + intel_vsec_dev->cap_id = cap_id; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; @@ -225,6 +324,101 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_name(header->id)); } +static bool suppliers_ready(struct vsec_priv *priv, + const struct vsec_feature_dependency *consumer_deps, + int cap_id) +{ + enum vsec_device_state *state = priv->state; + int supplier_id; + + if (WARN_ON_ONCE(consumer_deps->feature != BIT(cap_id))) + return false; + + /* + * Verify that all required suppliers have been found. Return false + * immediately if any are still missing. + */ + for_each_set_bit(supplier_id, &consumer_deps->supplier_bitmap, VSEC_FEATURE_COUNT) { + if (state[supplier_id] == STATE_SKIP) + continue; + + if (state[supplier_id] == STATE_NOT_FOUND) + return false; + } + + /* + * All suppliers have been found and the consumer is ready to be + * registered. + */ + return true; +} + +static int get_cap_id(u32 header_id, unsigned long *cap_id) +{ + switch (header_id) { + case VSEC_ID_TELEMETRY: + *cap_id = ilog2(VSEC_CAP_TELEMETRY); + break; + case VSEC_ID_WATCHER: + *cap_id = ilog2(VSEC_CAP_WATCHER); + break; + case VSEC_ID_CRASHLOG: + *cap_id = ilog2(VSEC_CAP_CRASHLOG); + break; + case VSEC_ID_SDSI: + *cap_id = ilog2(VSEC_CAP_SDSI); + break; + case VSEC_ID_TPMI: + *cap_id = ilog2(VSEC_CAP_TPMI); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int intel_vsec_register_device(struct pci_dev *pdev, + struct intel_vsec_header *header, + struct intel_vsec_platform_info *info) +{ + const struct vsec_feature_dependency *consumer_deps; + struct vsec_priv *priv; + unsigned long cap_id; + int ret; + + ret = get_cap_id(header->id, &cap_id); + if (ret) + return ret; + + /* + * Only track dependencies for devices probed by the VSEC driver. + * For others using the exported APIs, add the device directly. + */ + if (!pci_match_id(intel_vsec_pci_ids, pdev)) + return intel_vsec_add_dev(pdev, header, info, cap_id); + + priv = pci_get_drvdata(pdev); + if (priv->state[cap_id] == STATE_REGISTERED || + priv->state[cap_id] == STATE_SKIP) + return -EEXIST; + + priv->found_caps |= BIT(cap_id); + + consumer_deps = get_consumer_dependencies(priv, cap_id); + if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { + ret = intel_vsec_add_dev(pdev, header, info, cap_id); + if (ret) + priv->state[cap_id] = STATE_SKIP; + else + priv->state[cap_id] = STATE_REGISTERED; + + return ret; + } + + return -EAGAIN; +} + static bool intel_vsec_walk_header(struct pci_dev *pdev, struct intel_vsec_platform_info *info) { @@ -233,7 +427,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, int ret; for ( ; *header; header++) { - ret = intel_vsec_add_dev(pdev, *header, info); + ret = intel_vsec_register_device(pdev, *header, info); if (!ret) have_devices = true; } @@ -281,7 +475,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_add_dev(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info); if (ret) continue; @@ -326,7 +520,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_add_dev(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info); if (ret) continue; @@ -378,7 +572,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id { struct intel_vsec_platform_info *info; struct vsec_priv *priv; - int ret; + int num_caps, ret; + bool found_any = false; ret = pcim_enable_device(pdev); if (ret) @@ -396,7 +591,15 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id priv->info = info; pci_set_drvdata(pdev, priv); - if (!intel_vsec_get_features(pdev, info)) + num_caps = hweight_long(info->caps); + while (num_caps--) { + found_any |= intel_vsec_get_features(pdev, info); + + if (priv->found_caps == info->caps) + break; + } + + if (!found_any) return -ENODEV; return 0; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index bc95821f1bfb2..71067afaca992 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -5,11 +5,18 @@ #include #include -#define VSEC_CAP_TELEMETRY BIT(0) -#define VSEC_CAP_WATCHER BIT(1) -#define VSEC_CAP_CRASHLOG BIT(2) -#define VSEC_CAP_SDSI BIT(3) -#define VSEC_CAP_TPMI BIT(4) +/* + * VSEC_CAP_UNUSED is reserved. It exists to prevent zero initialized + * intel_vsec devices from being automatically set to a known + * capability with ID 0 + */ +#define VSEC_CAP_UNUSED BIT(0) +#define VSEC_CAP_TELEMETRY BIT(1) +#define VSEC_CAP_WATCHER BIT(2) +#define VSEC_CAP_CRASHLOG BIT(3) +#define VSEC_CAP_SDSI BIT(4) +#define VSEC_CAP_TPMI BIT(5) +#define VSEC_FEATURE_COUNT 6 /* Intel DVSEC offsets */ #define INTEL_DVSEC_ENTRIES 0xA @@ -81,22 +88,31 @@ struct pmt_callbacks { int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); }; +struct vsec_feature_dependency { + unsigned long feature; + unsigned long supplier_bitmap; +}; + /** * struct intel_vsec_platform_info - Platform specific data * @parent: parent device in the auxbus chain * @headers: list of headers to define the PMT client devices to create + * @deps: array of feature dependencies * @priv_data: private data, usable by parent devices, currently a callback * @caps: bitmask of PMT capabilities for the given headers * @quirks: bitmask of VSEC device quirks * @base_addr: allow a base address to be specified (rather than derived) + * @num_deps: Count feature dependencies */ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; + const struct vsec_feature_dependency *deps; void *priv_data; unsigned long caps; unsigned long quirks; u64 base_addr; + int num_deps; }; /** @@ -110,6 +126,7 @@ struct intel_vsec_platform_info { * @priv_data: any private data needed * @quirks: specified quirks * @base_addr: base address of entries (if specified) + * @cap_id: the enumerated id of the vsec feature */ struct intel_vsec_device { struct auxiliary_device auxdev; @@ -122,6 +139,7 @@ struct intel_vsec_device { size_t priv_data_size; unsigned long quirks; u64 base_addr; + unsigned long cap_id; }; int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, -- GitLab From 1f3855ea7d6b03f68c2eec7a0bcd537cedcc6680 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:20 -0700 Subject: [PATCH 388/868] platform/x86/intel/vsec: Skip absent features during initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some VSEC features depend on the presence of supplier features that may not always be present. To prevent unnecessary retries and device linking during initialization, introduce logic to skip attempts to link consumers to missing suppliers. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-6-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index aa1f7e63039d0..32f777b41b337 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -568,11 +568,30 @@ static bool intel_vsec_get_features(struct pci_dev *pdev, return found; } +static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) +{ + struct vsec_priv *priv = pci_get_drvdata(pdev); + const struct vsec_feature_dependency *deps = priv->info->deps; + int consumer_id = priv->info->num_deps; + + while (consumer_id--) { + int supplier_id; + + deps = &priv->info->deps[consumer_id]; + + for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) { + if (!(BIT(supplier_id) & priv->found_caps)) + priv->state[supplier_id] = STATE_SKIP; + } + } +} + static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct intel_vsec_platform_info *info; struct vsec_priv *priv; int num_caps, ret; + int run_once = 0; bool found_any = false; ret = pcim_enable_device(pdev); @@ -597,6 +616,11 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id if (priv->found_caps == info->caps) break; + + if (!run_once) { + intel_vsec_skip_missing_dependencies(pdev); + run_once = 1; + } } if (!found_any) -- GitLab From e4436e98672c7993cdfd7743efd0fcaa8df7cc17 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:21 -0700 Subject: [PATCH 389/868] platform/x86/intel/vsec: Skip driverless features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a feature lacks a corresponding driver and that feature is also a supplier, registering it would be prevent the consumer driver from probing. Introduces logic to skip such features during device registration. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-7-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 32f777b41b337..30e558af68887 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -123,6 +123,26 @@ get_consumer_dependencies(struct vsec_priv *priv, int cap_id) return NULL; } +static bool vsec_driver_present(int cap_id) +{ + unsigned long bit = BIT(cap_id); + + switch (bit) { + case VSEC_CAP_TELEMETRY: + return IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY); + case VSEC_CAP_WATCHER: + return IS_ENABLED(CONFIG_INTEL_PMT_WATCHER); + case VSEC_CAP_CRASHLOG: + return IS_ENABLED(CONFIG_INTEL_PMT_CRASHLOG); + case VSEC_CAP_SDSI: + return IS_ENABLED(CONFIG_INTEL_SDSI); + case VSEC_CAP_TPMI: + return IS_ENABLED(CONFIG_INTEL_TPMI); + default: + return false; + } +} + /* * Although pci_device_id table is available in the pdev, this prototype is * necessary because the code using it can be called by an exported API that @@ -158,7 +178,8 @@ static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, for_each_set_bit(supplier_id, &deps->supplier_bitmap, VSEC_FEATURE_COUNT) { struct device_link *link; - if (state[supplier_id] != STATE_REGISTERED) + if (state[supplier_id] != STATE_REGISTERED || + !vsec_driver_present(supplier_id)) continue; if (!suppliers[supplier_id]) { @@ -405,6 +426,11 @@ static int intel_vsec_register_device(struct pci_dev *pdev, priv->found_caps |= BIT(cap_id); + if (!vsec_driver_present(cap_id)) { + priv->state[cap_id] = STATE_SKIP; + return -ENODEV; + } + consumer_deps = get_consumer_dependencies(priv, cap_id); if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { ret = intel_vsec_add_dev(pdev, header, info, cap_id); -- GitLab From 10f32796e86c04f73b7f8580cc9483765ed19f49 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:22 -0700 Subject: [PATCH 390/868] platform/x86/intel/vsec: Add new Discovery feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the PCIe VSEC ID for new Intel Platform Monitoring Technology Capability Discovery feature. Discovery provides detailed information for the various Intel VSEC features. Also make the driver a supplier for TPMI and Telemetry drivers which will use the information. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-8-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 26 ++++++++++++++++++++++++-- include/linux/intel_vsec.h | 4 +++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 30e558af68887..4d76f1ac3c8c0 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -66,6 +66,9 @@ static const char *intel_vsec_name(enum intel_vsec_id id) case VSEC_ID_TPMI: return "tpmi"; + case VSEC_ID_DISCOVERY: + return "discovery"; + default: return NULL; } @@ -84,6 +87,8 @@ static bool intel_vsec_supported(u16 id, unsigned long caps) return !!(caps & VSEC_CAP_SDSI); case VSEC_ID_TPMI: return !!(caps & VSEC_CAP_TPMI); + case VSEC_ID_DISCOVERY: + return !!(caps & VSEC_CAP_DISCOVERY); default: return false; } @@ -138,6 +143,8 @@ static bool vsec_driver_present(int cap_id) return IS_ENABLED(CONFIG_INTEL_SDSI); case VSEC_CAP_TPMI: return IS_ENABLED(CONFIG_INTEL_TPMI); + case VSEC_CAP_DISCOVERY: + return IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY); default: return false; } @@ -392,6 +399,9 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) case VSEC_ID_TPMI: *cap_id = ilog2(VSEC_CAP_TPMI); break; + case VSEC_ID_DISCOVERY: + *cap_id = ilog2(VSEC_CAP_DISCOVERY); + break; default: return -EINVAL; } @@ -681,14 +691,26 @@ static const struct intel_vsec_platform_info mtl_info = { .caps = VSEC_CAP_TELEMETRY, }; +static const struct vsec_feature_dependency oobmsm_deps[] = { + { + .feature = VSEC_CAP_TELEMETRY, + .supplier_bitmap = VSEC_CAP_DISCOVERY, + }, +}; + /* OOBMSM info */ static const struct intel_vsec_platform_info oobmsm_info = { - .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI | + VSEC_CAP_DISCOVERY, + .deps = oobmsm_deps, + .num_deps = ARRAY_SIZE(oobmsm_deps), }; /* DMR OOBMSM info */ static const struct intel_vsec_platform_info dmr_oobmsm_info = { - .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI, + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI | VSEC_CAP_DISCOVERY, + .deps = oobmsm_deps, + .num_deps = ARRAY_SIZE(oobmsm_deps), }; /* TGL info */ diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 71067afaca992..a07796d7d43be 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -16,7 +16,8 @@ #define VSEC_CAP_CRASHLOG BIT(3) #define VSEC_CAP_SDSI BIT(4) #define VSEC_CAP_TPMI BIT(5) -#define VSEC_FEATURE_COUNT 6 +#define VSEC_CAP_DISCOVERY BIT(6) +#define VSEC_FEATURE_COUNT 7 /* Intel DVSEC offsets */ #define INTEL_DVSEC_ENTRIES 0xA @@ -33,6 +34,7 @@ enum intel_vsec_id { VSEC_ID_TELEMETRY = 2, VSEC_ID_WATCHER = 3, VSEC_ID_CRASHLOG = 4, + VSEC_ID_DISCOVERY = 12, VSEC_ID_SDSI = 65, VSEC_ID_TPMI = 66, }; -- GitLab From d9a0788093565c300f7c8dd034dbfa6ac4da9aa6 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:23 -0700 Subject: [PATCH 391/868] platform/x86/intel/pmt: Add PMT Discovery driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces a new driver to enumerate and expose Intel Platform Monitoring Technology (PMT) capabilities via a simple discovery mechanism. The PMT Discovery driver parses hardware-provided discovery tables from Intel Out of Band Management Services Modules (OOBMSM) and extracts feature information for various providers (such as TPMI, Telemetry, Crash Log, etc). This unified interface simplifies the process of determining which manageability and telemetry features are supported by a given platform. This new feature is described in the Intel Platform Monitoring Technology 3.0 specification, section 6.6 Capability. Key changes and additions: New file drivers/platform/x86/intel/pmt/discovery.c: – Implements the discovery logic to map the discovery resource, read the feature discovery table, and validate feature parameters. New file drivers/platform/x86/intel/pmt/features.c: – Defines feature names, layouts, and associated capability masks. – Provides a mapping between raw hardware attributes and sysfs representations for easier integration with user-space tools. New header include/linux/intel_pmt_features.h: – Declares constants, masks, and feature identifiers used across the PMT framework. Sysfs integration: – Feature attributes are exposed under /sys/class/intel_pmt. – Each device is represented by a subfolder within the intel_pmt class, named using its DBDF (Domain:Bus:Device.Function), e.g.: features-0000:00:03.1 – Example directory layout for a device: /sys/class/intel_pmt/features-0000:00:03.1/ ├── accelerator_telemetry ├── crash_log ├── per_core_environment_telemetry ├── per_core_performance_telemetry ├── per_rmid_energy_telemetry ├── per_rmid_perf_telemetry ├── tpmi_control ├── tracing └── uncore_telemetry By exposing PMT feature details through sysfs and integrating with the existing PMT class, this driver paves the way for more streamlined integration of PMT-based manageability and telemetry tools. Link: https://www.intel.com/content/www/us/en/content-details/710389/intel-platform-monitoring-technology-intel-pmt-external-specification.html Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-9-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/Kconfig | 12 + drivers/platform/x86/intel/pmt/Makefile | 2 + drivers/platform/x86/intel/pmt/class.c | 35 +- drivers/platform/x86/intel/pmt/class.h | 2 + drivers/platform/x86/intel/pmt/discovery.c | 602 +++++++++++++++++++++ drivers/platform/x86/intel/pmt/features.c | 205 +++++++ include/linux/intel_pmt_features.h | 157 ++++++ 7 files changed, 1013 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/x86/intel/pmt/discovery.c create mode 100644 drivers/platform/x86/intel/pmt/features.c create mode 100644 include/linux/intel_pmt_features.h diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index e916fc9662219..0ad91b5112e9d 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -38,3 +38,15 @@ config INTEL_PMT_CRASHLOG To compile this driver as a module, choose M here: the module will be called intel_pmt_crashlog. + +config INTEL_PMT_DISCOVERY + tristate "Intel Platform Monitoring Technology (PMT) Discovery driver" + depends on INTEL_VSEC + select INTEL_PMT_CLASS + help + The Intel Platform Monitoring Technology (PMT) discovery driver provides + access to details about the various PMT features and feature specific + attributes. + + To compile this driver as a module, choose M here: the module + will be called pmt_discovery. diff --git a/drivers/platform/x86/intel/pmt/Makefile b/drivers/platform/x86/intel/pmt/Makefile index 279e158c7c231..8aed7e1592e43 100644 --- a/drivers/platform/x86/intel/pmt/Makefile +++ b/drivers/platform/x86/intel/pmt/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_INTEL_PMT_TELEMETRY) += pmt_telemetry.o pmt_telemetry-y := telemetry.o obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o pmt_crashlog-y := crashlog.o +obj-$(CONFIG_INTEL_PMT_DISCOVERY) += pmt_discovery.o +pmt_discovery-y := discovery.o features.o diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 7233b654bbad1..a806a81ece520 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -9,11 +9,13 @@ */ #include +#include #include #include #include #include #include +#include #include "class.h" @@ -166,12 +168,41 @@ static struct attribute *intel_pmt_attrs[] = { &dev_attr_offset.attr, NULL }; -ATTRIBUTE_GROUPS(intel_pmt); -static struct class intel_pmt_class = { +static umode_t intel_pmt_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct auxiliary_device *auxdev = to_auxiliary_dev(dev->parent); + struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev); + + /* + * Place the discovery features folder in /sys/class/intel_pmt, but + * exclude the common attributes as they are not applicable. + */ + if (ivdev->cap_id == ilog2(VSEC_CAP_DISCOVERY)) + return 0; + + return attr->mode; +} + +static bool intel_pmt_group_visible(struct kobject *kobj) +{ + return true; +} +DEFINE_SYSFS_GROUP_VISIBLE(intel_pmt); + +static const struct attribute_group intel_pmt_group = { + .attrs = intel_pmt_attrs, + .is_visible = SYSFS_GROUP_VISIBLE(intel_pmt), +}; +__ATTRIBUTE_GROUPS(intel_pmt); + +struct class intel_pmt_class = { .name = "intel_pmt", .dev_groups = intel_pmt_groups, }; +EXPORT_SYMBOL_GPL(intel_pmt_class); static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index b2006d57779d6..39c32357ee2c9 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -20,6 +20,7 @@ #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) struct pci_dev; +extern struct class intel_pmt_class; struct telem_endpoint { struct pci_dev *pcidev; @@ -48,6 +49,7 @@ struct intel_pmt_entry { unsigned long base_addr; size_t size; u32 guid; + u32 num_rmids; /* Number of Resource Monitoring IDs */ int devid; }; diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c new file mode 100644 index 0000000000000..4b4fa3137ad2e --- /dev/null +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitory Technology Discovery driver + * + * Copyright (c) 2025, Intel Corporation. + * All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "class.h" + +#define MAX_FEATURE_VERSION 0 +#define DT_TBIR GENMASK(2, 0) +#define FEAT_ATTR_SIZE(x) ((x) * sizeof(u32)) +#define PMT_GUID_SIZE(x) ((x) * sizeof(u32)) +#define PMT_ACCESS_TYPE_RSVD 0xF +#define SKIP_FEATURE 1 + +struct feature_discovery_table { + u32 access_type:4; + u32 version:8; + u32 size:16; + u32 reserved:4; + u32 id; + u32 offset; + u32 reserved2; +}; + +/* Common feature table header */ +struct feature_header { + u32 attr_size:8; + u32 num_guids:8; + u32 reserved:16; +}; + +/* Feature attribute fields */ +struct caps { + u32 caps; +}; + +struct command { + u32 max_stream_size:16; + u32 max_command_size:16; +}; + +struct watcher { + u32 reserved:21; + u32 period:11; + struct command command; +}; + +struct rmid { + u32 num_rmids:16; /* Number of Resource Monitoring IDs */ + u32 reserved:16; + struct watcher watcher; +}; + +struct feature_table { + struct feature_header header; + struct caps caps; + union { + struct command command; + struct watcher watcher; + struct rmid rmid; + }; + u32 *guids; +}; + +/* For backreference in struct feature */ +struct pmt_features_priv; + +struct feature { + struct feature_table table; + struct kobject kobj; + struct pmt_features_priv *priv; + struct list_head list; + const struct attribute_group *attr_group; + enum pmt_feature_id id; +}; + +struct pmt_features_priv { + struct device *parent; + struct device *dev; + int count; + u32 mask; + struct feature feature[]; +}; + +static LIST_HEAD(pmt_feature_list); +static DEFINE_MUTEX(feature_list_lock); + +#define to_pmt_feature(x) container_of(x, struct feature, kobj) +static void pmt_feature_release(struct kobject *kobj) +{ +} + +static ssize_t caps_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + struct pmt_cap **pmt_caps; + u32 caps = feature->table.caps.caps; + ssize_t ret = 0; + + switch (feature->id) { + case FEATURE_PER_CORE_PERF_TELEM: + pmt_caps = pmt_caps_pcpt; + break; + case FEATURE_PER_CORE_ENV_TELEM: + pmt_caps = pmt_caps_pcet; + break; + case FEATURE_PER_RMID_PERF_TELEM: + pmt_caps = pmt_caps_rmid_perf; + break; + case FEATURE_ACCEL_TELEM: + pmt_caps = pmt_caps_accel; + break; + case FEATURE_UNCORE_TELEM: + pmt_caps = pmt_caps_uncore; + break; + case FEATURE_CRASH_LOG: + pmt_caps = pmt_caps_crashlog; + break; + case FEATURE_PETE_LOG: + pmt_caps = pmt_caps_pete; + break; + case FEATURE_TPMI_CTRL: + pmt_caps = pmt_caps_tpmi; + break; + case FEATURE_TRACING: + pmt_caps = pmt_caps_tracing; + break; + case FEATURE_PER_RMID_ENERGY_TELEM: + pmt_caps = pmt_caps_rmid_energy; + break; + default: + return -EINVAL; + } + + while (*pmt_caps) { + struct pmt_cap *pmt_cap = *pmt_caps; + + while (pmt_cap->name) { + ret += sysfs_emit_at(buf, ret, "%-40s Available: %s\n", pmt_cap->name, + str_yes_no(pmt_cap->mask & caps)); + pmt_cap++; + } + pmt_caps++; + } + + return ret; +} +static struct kobj_attribute caps_attribute = __ATTR_RO(caps); + +static struct watcher *get_watcher(struct feature *feature) +{ + switch (feature_layout[feature->id]) { + case LAYOUT_RMID: + return &feature->table.rmid.watcher; + case LAYOUT_WATCHER: + return &feature->table.watcher; + default: + return ERR_PTR(-EINVAL); + } +} + +static struct command *get_command(struct feature *feature) +{ + switch (feature_layout[feature->id]) { + case LAYOUT_RMID: + return &feature->table.rmid.watcher.command; + case LAYOUT_WATCHER: + return &feature->table.watcher.command; + case LAYOUT_COMMAND: + return &feature->table.command; + default: + return ERR_PTR(-EINVAL); + } +} + +static ssize_t num_rmids_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + + return sysfs_emit(buf, "%u\n", feature->table.rmid.num_rmids); +} +static struct kobj_attribute num_rmids_attribute = __ATTR_RO(num_rmids); + +static ssize_t min_watcher_period_ms_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + struct watcher *watcher = get_watcher(feature); + + if (IS_ERR(watcher)) + return PTR_ERR(watcher); + + return sysfs_emit(buf, "%u\n", watcher->period); +} +static struct kobj_attribute min_watcher_period_ms_attribute = + __ATTR_RO(min_watcher_period_ms); + +static ssize_t max_stream_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + struct command *command = get_command(feature); + + if (IS_ERR(command)) + return PTR_ERR(command); + + return sysfs_emit(buf, "%u\n", command->max_stream_size); +} +static struct kobj_attribute max_stream_size_attribute = + __ATTR_RO(max_stream_size); + +static ssize_t max_command_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + struct command *command = get_command(feature); + + if (IS_ERR(command)) + return PTR_ERR(command); + + return sysfs_emit(buf, "%u\n", command->max_command_size); +} +static struct kobj_attribute max_command_size_attribute = + __ATTR_RO(max_command_size); + +static ssize_t guids_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct feature *feature = to_pmt_feature(kobj); + int i, count = 0; + + for (i = 0; i < feature->table.header.num_guids; i++) + count += sysfs_emit_at(buf, count, "0x%x\n", + feature->table.guids[i]); + + return count; +} +static struct kobj_attribute guids_attribute = __ATTR_RO(guids); + +static struct attribute *pmt_feature_rmid_attrs[] = { + &caps_attribute.attr, + &num_rmids_attribute.attr, + &min_watcher_period_ms_attribute.attr, + &max_stream_size_attribute.attr, + &max_command_size_attribute.attr, + &guids_attribute.attr, + NULL +}; +ATTRIBUTE_GROUPS(pmt_feature_rmid); + +static const struct kobj_type pmt_feature_rmid_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = pmt_feature_release, + .default_groups = pmt_feature_rmid_groups, +}; + +static struct attribute *pmt_feature_watcher_attrs[] = { + &caps_attribute.attr, + &min_watcher_period_ms_attribute.attr, + &max_stream_size_attribute.attr, + &max_command_size_attribute.attr, + &guids_attribute.attr, + NULL +}; +ATTRIBUTE_GROUPS(pmt_feature_watcher); + +static const struct kobj_type pmt_feature_watcher_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = pmt_feature_release, + .default_groups = pmt_feature_watcher_groups, +}; + +static struct attribute *pmt_feature_command_attrs[] = { + &caps_attribute.attr, + &max_stream_size_attribute.attr, + &max_command_size_attribute.attr, + &guids_attribute.attr, + NULL +}; +ATTRIBUTE_GROUPS(pmt_feature_command); + +static const struct kobj_type pmt_feature_command_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = pmt_feature_release, + .default_groups = pmt_feature_command_groups, +}; + +static struct attribute *pmt_feature_guids_attrs[] = { + &caps_attribute.attr, + &guids_attribute.attr, + NULL +}; +ATTRIBUTE_GROUPS(pmt_feature_guids); + +static const struct kobj_type pmt_feature_guids_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = pmt_feature_release, + .default_groups = pmt_feature_guids_groups, +}; + +static int +pmt_feature_get_disc_table(struct pmt_features_priv *priv, + struct resource *disc_res, + struct feature_discovery_table *disc_tbl) +{ + void __iomem *disc_base; + + disc_base = devm_ioremap_resource(priv->dev, disc_res); + if (IS_ERR(disc_base)) + return PTR_ERR(disc_base); + + memcpy_fromio(disc_tbl, disc_base, sizeof(*disc_tbl)); + + devm_iounmap(priv->dev, disc_base); + + if (priv->mask & BIT(disc_tbl->id)) + return dev_err_probe(priv->dev, -EINVAL, "Duplicate feature: %s\n", + pmt_feature_names[disc_tbl->id]); + + /* + * Some devices may expose non-functioning entries that are + * reserved for future use. They have zero size. Do not fail + * probe for these. Just ignore them. + */ + if (disc_tbl->size == 0 || disc_tbl->access_type == PMT_ACCESS_TYPE_RSVD) + return SKIP_FEATURE; + + if (disc_tbl->version > MAX_FEATURE_VERSION) + return SKIP_FEATURE; + + if (!pmt_feature_id_is_valid(disc_tbl->id)) + return SKIP_FEATURE; + + priv->mask |= BIT(disc_tbl->id); + + return 0; +} + +static int +pmt_feature_get_feature_table(struct pmt_features_priv *priv, + struct feature *feature, + struct feature_discovery_table *disc_tbl, + struct resource *disc_res) +{ + struct feature_table *feat_tbl = &feature->table; + struct feature_header *header; + struct resource res = {}; + resource_size_t res_size; + void __iomem *feat_base, *feat_offset; + void *tbl_offset; + size_t size; + u32 *guids; + u8 tbir; + + tbir = FIELD_GET(DT_TBIR, disc_tbl->offset); + + switch (disc_tbl->access_type) { + case ACCESS_LOCAL: + if (tbir) + return dev_err_probe(priv->dev, -EINVAL, + "Unsupported BAR index %u for access type %u\n", + tbir, disc_tbl->access_type); + + + /* + * For access_type LOCAL, the base address is as follows: + * base address = end of discovery region + base offset + 1 + */ + res = DEFINE_RES_MEM(disc_res->end + disc_tbl->offset + 1, + disc_tbl->size * sizeof(u32)); + break; + + default: + return dev_err_probe(priv->dev, -EINVAL, "Unrecognized access_type %u\n", + disc_tbl->access_type); + } + + feature->id = disc_tbl->id; + + /* Get the feature table */ + feat_base = devm_ioremap_resource(priv->dev, &res); + if (IS_ERR(feat_base)) + return PTR_ERR(feat_base); + + feat_offset = feat_base; + tbl_offset = feat_tbl; + + /* Get the header */ + header = &feat_tbl->header; + memcpy_fromio(header, feat_offset, sizeof(*header)); + + /* Validate fields fit within mapped resource */ + size = sizeof(*header) + FEAT_ATTR_SIZE(header->attr_size) + + PMT_GUID_SIZE(header->num_guids); + res_size = resource_size(&res); + if (WARN(size > res_size, "Bad table size %ld > %pa", size, &res_size)) + return -EINVAL; + + /* Get the feature attributes, including capability fields */ + tbl_offset += sizeof(*header); + feat_offset += sizeof(*header); + + memcpy_fromio(tbl_offset, feat_offset, FEAT_ATTR_SIZE(header->attr_size)); + + /* Finally, get the guids */ + guids = devm_kmalloc(priv->dev, PMT_GUID_SIZE(header->num_guids), GFP_KERNEL); + if (!guids) + return -ENOMEM; + + feat_offset += FEAT_ATTR_SIZE(header->attr_size); + + memcpy_fromio(guids, feat_offset, PMT_GUID_SIZE(header->num_guids)); + + feat_tbl->guids = guids; + + devm_iounmap(priv->dev, feat_base); + + return 0; +} + +static void pmt_features_add_feat(struct feature *feature) +{ + guard(mutex)(&feature_list_lock); + list_add(&feature->list, &pmt_feature_list); +} + +static void pmt_features_remove_feat(struct feature *feature) +{ + guard(mutex)(&feature_list_lock); + list_del(&feature->list); +} + +/* Get the discovery table and use it to get the feature table */ +static int pmt_features_discovery(struct pmt_features_priv *priv, + struct feature *feature, + struct intel_vsec_device *ivdev, + int idx) +{ + struct feature_discovery_table disc_tbl = {}; /* Avoid false warning */ + struct resource *disc_res = &ivdev->resource[idx]; + const struct kobj_type *ktype; + int ret; + + ret = pmt_feature_get_disc_table(priv, disc_res, &disc_tbl); + if (ret) + return ret; + + ret = pmt_feature_get_feature_table(priv, feature, &disc_tbl, disc_res); + if (ret) + return ret; + + switch (feature_layout[feature->id]) { + case LAYOUT_RMID: + ktype = &pmt_feature_rmid_ktype; + feature->attr_group = &pmt_feature_rmid_group; + break; + case LAYOUT_WATCHER: + ktype = &pmt_feature_watcher_ktype; + feature->attr_group = &pmt_feature_watcher_group; + break; + case LAYOUT_COMMAND: + ktype = &pmt_feature_command_ktype; + feature->attr_group = &pmt_feature_command_group; + break; + case LAYOUT_CAPS_ONLY: + ktype = &pmt_feature_guids_ktype; + feature->attr_group = &pmt_feature_guids_group; + break; + default: + return -EINVAL; + } + + ret = kobject_init_and_add(&feature->kobj, ktype, &priv->dev->kobj, + pmt_feature_names[feature->id]); + if (ret) + return ret; + + kobject_uevent(&feature->kobj, KOBJ_ADD); + pmt_features_add_feat(feature); + + return 0; +} + +static void pmt_features_remove(struct auxiliary_device *auxdev) +{ + struct pmt_features_priv *priv = auxiliary_get_drvdata(auxdev); + int i; + + for (i = 0; i < priv->count; i++) { + struct feature *feature = &priv->feature[i]; + + pmt_features_remove_feat(feature); + sysfs_remove_group(&feature->kobj, feature->attr_group); + kobject_put(&feature->kobj); + } + + device_unregister(priv->dev); +} + +static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) +{ + struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev); + struct pmt_features_priv *priv; + size_t size; + int ret, i; + + size = struct_size(priv, feature, ivdev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->parent = &ivdev->pcidev->dev; + auxiliary_set_drvdata(auxdev, priv); + + priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv, + "%s-%s", "features", dev_name(priv->parent)); + if (IS_ERR(priv->dev)) + return dev_err_probe(priv->dev, PTR_ERR(priv->dev), + "Could not create %s-%s device node\n", + "features", dev_name(priv->dev)); + + /* Initialize each feature */ + for (i = 0; i < ivdev->num_resources; i++) { + struct feature *feature = &priv->feature[priv->count]; + + ret = pmt_features_discovery(priv, feature, ivdev, i); + if (ret == SKIP_FEATURE) + continue; + if (ret != 0) + goto abort_probe; + + feature->priv = priv; + priv->count++; + } + + return 0; + +abort_probe: + /* + * Only fully initialized features are tracked in priv->count, which is + * incremented only after a feature is completely set up (i.e., after + * discovery and sysfs registration). If feature initialization fails, + * the failing feature's state is local and does not require rollback. + * + * Therefore, on error, we can safely call the driver's remove() routine + * pmt_features_remove() to clean up only those features that were + * fully initialized and counted. All other resources are device-managed + * and will be cleaned up automatically during device_unregister(). + */ + pmt_features_remove(auxdev); + + return ret; +} + +static const struct auxiliary_device_id pmt_features_id_table[] = { + { .name = "intel_vsec.discovery" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_features_id_table); + +static struct auxiliary_driver pmt_features_aux_driver = { + .id_table = pmt_features_id_table, + .remove = pmt_features_remove, + .probe = pmt_features_probe, +}; +module_auxiliary_driver(pmt_features_aux_driver); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMT Discovery driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_PMT"); diff --git a/drivers/platform/x86/intel/pmt/features.c b/drivers/platform/x86/intel/pmt/features.c new file mode 100644 index 0000000000000..8a39cddc75c8c --- /dev/null +++ b/drivers/platform/x86/intel/pmt/features.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Intel Corporation. + * All Rights Reserved. + * + * Author: "David E. Box" + */ + +#include +#include + +#include + +const char * const pmt_feature_names[] = { + [FEATURE_PER_CORE_PERF_TELEM] = "per_core_performance_telemetry", + [FEATURE_PER_CORE_ENV_TELEM] = "per_core_environment_telemetry", + [FEATURE_PER_RMID_PERF_TELEM] = "per_rmid_perf_telemetry", + [FEATURE_ACCEL_TELEM] = "accelerator_telemetry", + [FEATURE_UNCORE_TELEM] = "uncore_telemetry", + [FEATURE_CRASH_LOG] = "crash_log", + [FEATURE_PETE_LOG] = "pete_log", + [FEATURE_TPMI_CTRL] = "tpmi_control", + [FEATURE_TRACING] = "tracing", + [FEATURE_PER_RMID_ENERGY_TELEM] = "per_rmid_energy_telemetry", +}; +EXPORT_SYMBOL_NS_GPL(pmt_feature_names, "INTEL_PMT_DISCOVERY"); + +enum feature_layout feature_layout[] = { + [FEATURE_PER_CORE_PERF_TELEM] = LAYOUT_WATCHER, + [FEATURE_PER_CORE_ENV_TELEM] = LAYOUT_WATCHER, + [FEATURE_PER_RMID_PERF_TELEM] = LAYOUT_RMID, + [FEATURE_ACCEL_TELEM] = LAYOUT_WATCHER, + [FEATURE_UNCORE_TELEM] = LAYOUT_WATCHER, + [FEATURE_CRASH_LOG] = LAYOUT_COMMAND, + [FEATURE_PETE_LOG] = LAYOUT_COMMAND, + [FEATURE_TPMI_CTRL] = LAYOUT_CAPS_ONLY, + [FEATURE_TRACING] = LAYOUT_CAPS_ONLY, + [FEATURE_PER_RMID_ENERGY_TELEM] = LAYOUT_RMID, +}; + +struct pmt_cap pmt_cap_common[] = { + {PMT_CAP_TELEM, "telemetry"}, + {PMT_CAP_WATCHER, "watcher"}, + {PMT_CAP_CRASHLOG, "crashlog"}, + {PMT_CAP_STREAMING, "streaming"}, + {PMT_CAP_THRESHOLD, "threshold"}, + {PMT_CAP_WINDOW, "window"}, + {PMT_CAP_CONFIG, "config"}, + {PMT_CAP_TRACING, "tracing"}, + {PMT_CAP_INBAND, "inband"}, + {PMT_CAP_OOB, "oob"}, + {PMT_CAP_SECURED_CHAN, "secure_chan"}, + {PMT_CAP_PMT_SP, "pmt_sp"}, + {PMT_CAP_PMT_SP_POLICY, "pmt_sp_policy"}, + {} +}; + +struct pmt_cap pmt_cap_pcpt[] = { + {PMT_CAP_PCPT_CORE_PERF, "core_performance"}, + {PMT_CAP_PCPT_CORE_C0_RES, "core_c0_residency"}, + {PMT_CAP_PCPT_CORE_ACTIVITY, "core_activity"}, + {PMT_CAP_PCPT_CACHE_PERF, "cache_performance"}, + {PMT_CAP_PCPT_QUALITY_TELEM, "quality_telemetry"}, + {} +}; + +struct pmt_cap *pmt_caps_pcpt[] = { + pmt_cap_common, + pmt_cap_pcpt, + NULL +}; + +struct pmt_cap pmt_cap_pcet[] = { + {PMT_CAP_PCET_WORKPOINT_HIST, "workpoint_histogram"}, + {PMT_CAP_PCET_CORE_CURR_TEMP, "core_current_temp"}, + {PMT_CAP_PCET_CORE_INST_RES, "core_inst_residency"}, + {PMT_CAP_PCET_QUALITY_TELEM, "quality_telemetry"}, + {PMT_CAP_PCET_CORE_CDYN_LVL, "core_cdyn_level"}, + {PMT_CAP_PCET_CORE_STRESS_LVL, "core_stress_level"}, + {PMT_CAP_PCET_CORE_DAS, "core_digital_aging_sensor"}, + {PMT_CAP_PCET_FIVR_HEALTH, "fivr_health"}, + {PMT_CAP_PCET_ENERGY, "energy"}, + {PMT_CAP_PCET_PEM_STATUS, "pem_status"}, + {PMT_CAP_PCET_CORE_C_STATE, "core_c_state"}, + {} +}; + +struct pmt_cap *pmt_caps_pcet[] = { + pmt_cap_common, + pmt_cap_pcet, + NULL +}; + +struct pmt_cap pmt_cap_rmid_perf[] = { + {PMT_CAP_RMID_CORES_PERF, "core_performance"}, + {PMT_CAP_RMID_CACHE_PERF, "cache_performance"}, + {PMT_CAP_RMID_PERF_QUAL, "performance_quality"}, + {} +}; + +struct pmt_cap *pmt_caps_rmid_perf[] = { + pmt_cap_common, + pmt_cap_rmid_perf, + NULL +}; + +struct pmt_cap pmt_cap_accel[] = { + {PMT_CAP_ACCEL_CPM_TELEM, "content_processing_module"}, + {PMT_CAP_ACCEL_TIP_TELEM, "content_turbo_ip"}, + {} +}; + +struct pmt_cap *pmt_caps_accel[] = { + pmt_cap_common, + pmt_cap_accel, + NULL +}; + +struct pmt_cap pmt_cap_uncore[] = { + {PMT_CAP_UNCORE_IO_CA_TELEM, "io_ca"}, + {PMT_CAP_UNCORE_RMID_TELEM, "rmid"}, + {PMT_CAP_UNCORE_D2D_ULA_TELEM, "d2d_ula"}, + {PMT_CAP_UNCORE_PKGC_TELEM, "package_c"}, + {} +}; + +struct pmt_cap *pmt_caps_uncore[] = { + pmt_cap_common, + pmt_cap_uncore, + NULL +}; + +struct pmt_cap pmt_cap_crashlog[] = { + {PMT_CAP_CRASHLOG_MAN_TRIG, "manual_trigger"}, + {PMT_CAP_CRASHLOG_CORE, "core"}, + {PMT_CAP_CRASHLOG_UNCORE, "uncore"}, + {PMT_CAP_CRASHLOG_TOR, "tor"}, + {PMT_CAP_CRASHLOG_S3M, "s3m"}, + {PMT_CAP_CRASHLOG_PERSISTENCY, "persistency"}, + {PMT_CAP_CRASHLOG_CLIP_GPIO, "crashlog_in_progress"}, + {PMT_CAP_CRASHLOG_PRE_RESET, "pre_reset_extraction"}, + {PMT_CAP_CRASHLOG_POST_RESET, "post_reset_extraction"}, + {} +}; + +struct pmt_cap *pmt_caps_crashlog[] = { + pmt_cap_common, + pmt_cap_crashlog, + NULL +}; + +struct pmt_cap pmt_cap_pete[] = { + {PMT_CAP_PETE_MAN_TRIG, "manual_trigger"}, + {PMT_CAP_PETE_ENCRYPTION, "encryption"}, + {PMT_CAP_PETE_PERSISTENCY, "persistency"}, + {PMT_CAP_PETE_REQ_TOKENS, "required_tokens"}, + {PMT_CAP_PETE_PROD_ENABLED, "production_enabled"}, + {PMT_CAP_PETE_DEBUG_ENABLED, "debug_enabled"}, + {} +}; + +struct pmt_cap *pmt_caps_pete[] = { + pmt_cap_common, + pmt_cap_pete, + NULL +}; + +struct pmt_cap pmt_cap_tpmi[] = { + {PMT_CAP_TPMI_MAILBOX, "mailbox"}, + {PMT_CAP_TPMI_LOCK, "bios_lock"}, + {} +}; + +struct pmt_cap *pmt_caps_tpmi[] = { + pmt_cap_common, + pmt_cap_tpmi, + NULL +}; + +struct pmt_cap pmt_cap_tracing[] = { + {PMT_CAP_TRACE_SRAR, "srar_errors"}, + {PMT_CAP_TRACE_CORRECTABLE, "correctable_errors"}, + {PMT_CAP_TRACE_MCTP, "mctp"}, + {PMT_CAP_TRACE_MRT, "memory_resiliency"}, + {} +}; + +struct pmt_cap *pmt_caps_tracing[] = { + pmt_cap_common, + pmt_cap_tracing, + NULL +}; + +struct pmt_cap pmt_cap_rmid_energy[] = { + {PMT_CAP_RMID_ENERGY, "energy"}, + {PMT_CAP_RMID_ACTIVITY, "activity"}, + {PMT_CAP_RMID_ENERGY_QUAL, "energy_quality"}, + {} +}; + +struct pmt_cap *pmt_caps_rmid_energy[] = { + pmt_cap_common, + pmt_cap_rmid_energy, + NULL +}; diff --git a/include/linux/intel_pmt_features.h b/include/linux/intel_pmt_features.h new file mode 100644 index 0000000000000..53573a4a49b78 --- /dev/null +++ b/include/linux/intel_pmt_features.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _FEATURES_H +#define _FEATURES_H + +#include +#include + +/* Common masks */ +#define PMT_CAP_TELEM BIT(0) +#define PMT_CAP_WATCHER BIT(1) +#define PMT_CAP_CRASHLOG BIT(2) +#define PMT_CAP_STREAMING BIT(3) +#define PMT_CAP_THRESHOLD BIT(4) +#define PMT_CAP_WINDOW BIT(5) +#define PMT_CAP_CONFIG BIT(6) +#define PMT_CAP_TRACING BIT(7) +#define PMT_CAP_INBAND BIT(8) +#define PMT_CAP_OOB BIT(9) +#define PMT_CAP_SECURED_CHAN BIT(10) + +#define PMT_CAP_PMT_SP BIT(11) +#define PMT_CAP_PMT_SP_POLICY GENMASK(17, 12) + +/* Per Core Performance Telemetry (PCPT) specific masks */ +#define PMT_CAP_PCPT_CORE_PERF BIT(18) +#define PMT_CAP_PCPT_CORE_C0_RES BIT(19) +#define PMT_CAP_PCPT_CORE_ACTIVITY BIT(20) +#define PMT_CAP_PCPT_CACHE_PERF BIT(21) +#define PMT_CAP_PCPT_QUALITY_TELEM BIT(22) + +/* Per Core Environmental Telemetry (PCET) specific masks */ +#define PMT_CAP_PCET_WORKPOINT_HIST BIT(18) +#define PMT_CAP_PCET_CORE_CURR_TEMP BIT(19) +#define PMT_CAP_PCET_CORE_INST_RES BIT(20) +#define PMT_CAP_PCET_QUALITY_TELEM BIT(21) /* Same as PMT_CAP_PCPT */ +#define PMT_CAP_PCET_CORE_CDYN_LVL BIT(22) +#define PMT_CAP_PCET_CORE_STRESS_LVL BIT(23) +#define PMT_CAP_PCET_CORE_DAS BIT(24) +#define PMT_CAP_PCET_FIVR_HEALTH BIT(25) +#define PMT_CAP_PCET_ENERGY BIT(26) +#define PMT_CAP_PCET_PEM_STATUS BIT(27) +#define PMT_CAP_PCET_CORE_C_STATE BIT(28) + +/* Per RMID Performance Telemetry specific masks */ +#define PMT_CAP_RMID_CORES_PERF BIT(18) +#define PMT_CAP_RMID_CACHE_PERF BIT(19) +#define PMT_CAP_RMID_PERF_QUAL BIT(20) + +/* Accelerator Telemetry specific masks */ +#define PMT_CAP_ACCEL_CPM_TELEM BIT(18) +#define PMT_CAP_ACCEL_TIP_TELEM BIT(19) + +/* Uncore Telemetry specific masks */ +#define PMT_CAP_UNCORE_IO_CA_TELEM BIT(18) +#define PMT_CAP_UNCORE_RMID_TELEM BIT(19) +#define PMT_CAP_UNCORE_D2D_ULA_TELEM BIT(20) +#define PMT_CAP_UNCORE_PKGC_TELEM BIT(21) + +/* Crash Log specific masks */ +#define PMT_CAP_CRASHLOG_MAN_TRIG BIT(11) +#define PMT_CAP_CRASHLOG_CORE BIT(12) +#define PMT_CAP_CRASHLOG_UNCORE BIT(13) +#define PMT_CAP_CRASHLOG_TOR BIT(14) +#define PMT_CAP_CRASHLOG_S3M BIT(15) +#define PMT_CAP_CRASHLOG_PERSISTENCY BIT(16) +#define PMT_CAP_CRASHLOG_CLIP_GPIO BIT(17) +#define PMT_CAP_CRASHLOG_PRE_RESET BIT(18) +#define PMT_CAP_CRASHLOG_POST_RESET BIT(19) + +/* PeTe Log specific masks */ +#define PMT_CAP_PETE_MAN_TRIG BIT(11) +#define PMT_CAP_PETE_ENCRYPTION BIT(12) +#define PMT_CAP_PETE_PERSISTENCY BIT(13) +#define PMT_CAP_PETE_REQ_TOKENS BIT(14) +#define PMT_CAP_PETE_PROD_ENABLED BIT(15) +#define PMT_CAP_PETE_DEBUG_ENABLED BIT(16) + +/* TPMI control specific masks */ +#define PMT_CAP_TPMI_MAILBOX BIT(11) +#define PMT_CAP_TPMI_LOCK BIT(12) + +/* Tracing specific masks */ +#define PMT_CAP_TRACE_SRAR BIT(11) +#define PMT_CAP_TRACE_CORRECTABLE BIT(12) +#define PMT_CAP_TRACE_MCTP BIT(13) +#define PMT_CAP_TRACE_MRT BIT(14) + +/* Per RMID Energy Telemetry specific masks */ +#define PMT_CAP_RMID_ENERGY BIT(18) +#define PMT_CAP_RMID_ACTIVITY BIT(19) +#define PMT_CAP_RMID_ENERGY_QUAL BIT(20) + +enum pmt_feature_id { + FEATURE_INVALID = 0x0, + FEATURE_PER_CORE_PERF_TELEM = 0x1, + FEATURE_PER_CORE_ENV_TELEM = 0x2, + FEATURE_PER_RMID_PERF_TELEM = 0x3, + FEATURE_ACCEL_TELEM = 0x4, + FEATURE_UNCORE_TELEM = 0x5, + FEATURE_CRASH_LOG = 0x6, + FEATURE_PETE_LOG = 0x7, + FEATURE_TPMI_CTRL = 0x8, + FEATURE_RESERVED = 0x9, + FEATURE_TRACING = 0xA, + FEATURE_PER_RMID_ENERGY_TELEM = 0xB, + FEATURE_MAX = 0xB, +}; + +enum feature_layout { + LAYOUT_RMID, + LAYOUT_WATCHER, + LAYOUT_COMMAND, + LAYOUT_CAPS_ONLY, +}; + +struct pmt_cap { + u32 mask; + const char *name; +}; + +extern const char * const pmt_feature_names[]; +extern enum feature_layout feature_layout[]; +extern struct pmt_cap pmt_cap_common[]; +extern struct pmt_cap pmt_cap_pcpt[]; +extern struct pmt_cap *pmt_caps_pcpt[]; +extern struct pmt_cap pmt_cap_pcet[]; +extern struct pmt_cap *pmt_caps_pcet[]; +extern struct pmt_cap pmt_cap_rmid_perf[]; +extern struct pmt_cap *pmt_caps_rmid_perf[]; +extern struct pmt_cap pmt_cap_accel[]; +extern struct pmt_cap *pmt_caps_accel[]; +extern struct pmt_cap pmt_cap_uncore[]; +extern struct pmt_cap *pmt_caps_uncore[]; +extern struct pmt_cap pmt_cap_crashlog[]; +extern struct pmt_cap *pmt_caps_crashlog[]; +extern struct pmt_cap pmt_cap_pete[]; +extern struct pmt_cap *pmt_caps_pete[]; +extern struct pmt_cap pmt_cap_tpmi[]; +extern struct pmt_cap *pmt_caps_tpmi[]; +extern struct pmt_cap pmt_cap_s3m[]; +extern struct pmt_cap *pmt_caps_s3m[]; +extern struct pmt_cap pmt_cap_tracing[]; +extern struct pmt_cap *pmt_caps_tracing[]; +extern struct pmt_cap pmt_cap_rmid_energy[]; +extern struct pmt_cap *pmt_caps_rmid_energy[]; + +static inline bool pmt_feature_id_is_valid(enum pmt_feature_id id) +{ + if (id > FEATURE_MAX) + return false; + + if (id == FEATURE_INVALID || id == FEATURE_RESERVED) + return false; + + return true; +} +#endif -- GitLab From 2e7ba52110ef15d29846b40eb28b400f1fb1834a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:24 -0700 Subject: [PATCH 392/868] docs: Add ABI documentation for intel_pmt feature directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new sysfs ABI documentation file describing the layout and content of the features-/ directory used by Intel PMT (Platform Monitoring Technology). This directory exposes telemetry and control feature details for a given PMT PCI device. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-10-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../testing/sysfs-class-intel_pmt-features | 134 ++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 135 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-intel_pmt-features diff --git a/Documentation/ABI/testing/sysfs-class-intel_pmt-features b/Documentation/ABI/testing/sysfs-class-intel_pmt-features new file mode 100644 index 0000000000000..cddb30e5bdf68 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-intel_pmt-features @@ -0,0 +1,134 @@ +What: /sys/class/intel_pmt/features-/ +Date: 2025-04-24 +KernelVersion: 6.16 +Contact: david.e.box@linux.intel.com +Description: + The `features-/` directory represents the "features" + capability exposed by Intel PMT (Platform Monitoring Technology) + for the given PCI device. + + Each directory corresponds to a PMT feature and contains + attributes describing the available telemetry, monitoring, or + control functionalities. + +Directory Structure: + + /sys/class/intel_pmt/features-/ + ├── accelerator_telemetry/ # Per-accelerator telemetry data + ├── crash_log/ # Contains system crash telemetry logs + ├── per_core_environment_telemetry/ # Environmental telemetry per core + ├── per_core_performance_telemetry/ # Performance telemetry per core + ├── per_rmid_energy_telemetry/ # Energy telemetry for RMIDs + ├── per_rmid_perf_telemetry/ # Performance telemetry for RMIDs + ├── tpmi_control/ # TPMI-related controls and telemetry + ├── tracing/ # PMT tracing features + └── uncore_telemetry/ # Uncore telemetry data + +Common Files (Present in all feature directories): + + caps + - Read-only + - Lists available capabilities for this feature. + + guids + - Read-only + - Lists GUIDs associated with this feature. + +Additional Attributes (Conditional Presence): + + max_command_size + - Read-only + - Present if the feature supports out-of-band MCTP access. + - Maximum supported MCTP command size for out-of-band PMT access (bytes). + + max_stream_size + - Read-only + - Present if the feature supports out-of-band MCTP access. + - Maximum supported MCTP stream size (bytes). + + min_watcher_period_ms + - Read-only + - Present if the feature supports the watcher API. + The watcher API provides a writable control interface that allows user + configuration of monitoring behavior, such as setting the sampling or + reporting interval. + - Minimum supported time period for the watcher interface (milliseconds). + + num_rmids + - Read-only + - Present if the feature supports RMID (Resource Monitoring ID) telemetry. + RMIDs are identifiers used by hardware to track and report resource usage, + such as memory bandwidth or energy consumption, on a per-logical-entity + basis (e.g., per core, thread, or process group). + - Maximum number of RMIDs tracked simultaneously. + +Example: +For a device with PCI BDF `0000:00:03.1`, the directory tree could look like: + + /sys/class/intel_pmt/features-0000:00:03.1/ + ├── accelerator_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + ├── crash_log/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + ├── per_core_environment_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + ├── per_rmid_energy_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + │ ├── num_rmids + ├── tpmi_control/ + │ ├── caps + │ ├── guids + ├── tracing/ + │ ├── caps + │ ├── guids + ├── uncore_telemetry/ + │ ├── caps + │ ├── guids + │ ├── max_command_size + │ ├── max_stream_size + │ ├── min_watcher_period_ms + +Notes: + - Some attributes are only present if the corresponding feature supports + the capability (e.g., `max_command_size` for MCTP-capable features). + - Features supporting RMIDs include `num_rmids`. + - Features supporting the watcher API include `min_watcher_period_ms`. + - The `caps` file provides additional information about the functionality + of the feature. + +Example 'caps' content for the 'tracing' feature: + + /sys/class/intel_pmt/features-0000:00:03.1/ + ├── tracing/ + │ ├── caps + + telemetry Available: No + watcher Available: Yes + crashlog Available: No + streaming Available: No + threashold Available: No + window Available: No + config Available: Yes + tracing Available: No + inband Available: Yes + oob Available: Yes + secure_chan Available: No + pmt_sp Available: Yes + pmt_sp_policy Available: Yes + mailbox Available: Yes + bios_lock Available: Yes diff --git a/MAINTAINERS b/MAINTAINERS index 4af5a8ee40579..0a5309abba307 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12387,6 +12387,7 @@ INTEL PMT DRIVERS M: David E. Box S: Supported F: Documentation/ABI/testing/sysfs-class-intel_pmt +F: Documentation/ABI/testing/sysfs-class-intel_pmt-features F: drivers/platform/x86/intel/pmt/ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT -- GitLab From 934954df0f44de5e10afc1af84c06f78149f15fe Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:25 -0700 Subject: [PATCH 393/868] platform/x86/intel/tpmi: Relocate platform info to intel_vsec.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TPMI platform information provides a mapping of OOBMSM PCI devices to logical CPUs. Since this mapping is consistent across all OOBMSM features (e.g., TPMI, PMT, SDSi), it can be leveraged by multiple drivers. To facilitate reuse, relocate the struct intel_tpmi_plat_info to intel_vsec.h, renaming it to struct oobmsm_plat_info, making it accessible to other features. While modifying headers, place them in alphabetical order. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20250703022832.1302928-11-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/plr_tpmi.c | 3 ++- .../intel/speed_select_if/isst_tpmi_core.c | 9 ++++--- .../uncore-frequency/uncore-frequency-tpmi.c | 7 ++--- drivers/platform/x86/intel/vsec_tpmi.c | 4 +-- drivers/powercap/intel_rapl_tpmi.c | 9 ++++--- include/linux/intel_tpmi.h | 27 +++---------------- include/linux/intel_vsec.h | 22 +++++++++++++++ 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/drivers/platform/x86/intel/plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c index 2b55347a5a93b..58132da477457 100644 --- a/drivers/platform/x86/intel/plr_tpmi.c +++ b/drivers/platform/x86/intel/plr_tpmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -256,7 +257,7 @@ DEFINE_SHOW_STORE_ATTRIBUTE(plr_status); static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; struct dentry *dentry; int i, num_resources; struct resource *res; diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 18c035710eb94..34bff2f65a835 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1546,7 +1547,7 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev) { struct tpmi_per_power_domain_info *pd_info; bool read_blocked = 0, write_blocked = 0; - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; struct device *dev = &auxdev->dev; struct tpmi_sst_struct *tpmi_sst; u8 i, num_resources, io_die_cnt; @@ -1698,7 +1699,7 @@ EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_add, "INTEL_TPMI_SST"); void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) { struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; plat_info = tpmi_get_platform_data(auxdev); if (!plat_info) @@ -1720,7 +1721,7 @@ void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev) { struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); struct tpmi_per_power_domain_info *power_domain_info; - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; void __iomem *cp_base; plat_info = tpmi_get_platform_data(auxdev); @@ -1748,7 +1749,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) { struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); struct tpmi_per_power_domain_info *power_domain_info; - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; void __iomem *cp_base; plat_info = tpmi_get_platform_data(auxdev); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 44d9948ed2241..6df55c8e16b79 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -22,9 +22,10 @@ #include #include #include +#include +#include #include #include -#include #include "../tpmi_power_domains.h" #include "uncore-frequency-common.h" @@ -448,7 +449,7 @@ static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore) } static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_info, - struct intel_tpmi_plat_info *plat_info) + struct oobmsm_plat_info *plat_info) { cluster_info->cdie_id = domain_id; @@ -465,7 +466,7 @@ static void set_cdie_id(int domain_id, struct tpmi_uncore_cluster_info *cluster_ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { bool read_blocked = 0, write_blocked = 0; - struct intel_tpmi_plat_info *plat_info; + struct oobmsm_plat_info *plat_info; struct tpmi_uncore_struct *tpmi_uncore; bool uncore_sysfs_added = false; int ret, i, pkg = 0; diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 5c383a27bbe8d..d95a0d994546f 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -116,7 +116,7 @@ struct intel_tpmi_info { struct intel_vsec_device *vsec_dev; int feature_count; u64 pfs_start; - struct intel_tpmi_plat_info plat_info; + struct oobmsm_plat_info plat_info; void __iomem *tpmi_control_mem; struct dentry *dbgfs_dir; }; @@ -187,7 +187,7 @@ struct tpmi_feature_state { /* Used during auxbus device creation */ static DEFINE_IDA(intel_vsec_tpmi_ida); -struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) +struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) { struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); diff --git a/drivers/powercap/intel_rapl_tpmi.c b/drivers/powercap/intel_rapl_tpmi.c index af2368f4db10a..82201bf4685d4 100644 --- a/drivers/powercap/intel_rapl_tpmi.c +++ b/drivers/powercap/intel_rapl_tpmi.c @@ -9,9 +9,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include -#include #include +#include +#include +#include #include #include @@ -48,7 +49,7 @@ enum tpmi_rapl_register { struct tpmi_rapl_package { struct rapl_if_priv priv; - struct intel_tpmi_plat_info *tpmi_info; + struct oobmsm_plat_info *tpmi_info; struct rapl_package *rp; void __iomem *base; struct list_head node; @@ -253,7 +254,7 @@ static int intel_rapl_tpmi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { struct tpmi_rapl_package *trp; - struct intel_tpmi_plat_info *info; + struct oobmsm_plat_info *info; struct resource *res; u32 offset; int ret; diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index ff480b47ae642..94c06bf214fb6 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -8,6 +8,8 @@ #include +struct oobmsm_plat_info; + #define TPMI_VERSION_INVALID 0xff #define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val) #define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val) @@ -26,30 +28,7 @@ enum intel_tpmi_id { TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ }; -/** - * struct intel_tpmi_plat_info - Platform information for a TPMI device instance - * @cdie_mask: Mask of all compute dies in the partition - * @package_id: CPU Package id - * @partition: Package partition id when multiple VSEC PCI devices per package - * @segment: PCI segment ID - * @bus_number: PCI bus number - * @device_number: PCI device number - * @function_number: PCI function number - * - * Structure to store platform data for a TPMI device instance. This - * struct is used to return data via tpmi_get_platform_data(). - */ -struct intel_tpmi_plat_info { - u16 cdie_mask; - u8 package_id; - u8 partition; - u8 segment; - u8 bus_number; - u8 device_number; - u8 function_number; -}; - -struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); +struct oobmsm_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index); int tpmi_get_resource_count(struct auxiliary_device *auxdev); int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked, diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index a07796d7d43be..cd78d0b2e623c 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -144,6 +144,28 @@ struct intel_vsec_device { unsigned long cap_id; }; +/** + * struct oobmsm_plat_info - Platform information for a device instance + * @cdie_mask: Mask of all compute dies in the partition + * @package_id: CPU Package id + * @partition: Package partition id when multiple VSEC PCI devices per package + * @segment: PCI segment ID + * @bus_number: PCI bus number + * @device_number: PCI device number + * @function_number: PCI function number + * + * Structure to store platform data for a OOBMSM device instance. + */ +struct oobmsm_plat_info { + u16 cdie_mask; + u8 package_id; + u8 partition; + u8 segment; + u8 bus_number; + u8 device_number; + u8 function_number; +}; + int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name); -- GitLab From a885a2780937afac4f31f00d11663f50d05dfb35 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:26 -0700 Subject: [PATCH 394/868] platform/x86/intel/vsec: Set OOBMSM to CPU mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add functions, intel_vsec_set/get_mapping(), to set and retrieve the OOBMSM-to-CPU mapping data in the private data of the parent Intel VSEC driver. With this mapping information available, other Intel VSEC features on the same OOBMSM device can easily access and use the mapping data, allowing each of the OOBMSM features to map to the CPUs they provides data for. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-12-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 31 +++++++++++++++++++++++++++++++ include/linux/intel_vsec.h | 12 ++++++++++++ 2 files changed, 43 insertions(+) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 4d76f1ac3c8c0..711ff4edfe210 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -44,6 +44,7 @@ enum vsec_device_state { struct vsec_priv { struct intel_vsec_platform_info *info; struct device *suppliers[VSEC_FEATURE_COUNT]; + struct oobmsm_plat_info plat_info; enum vsec_device_state state[VSEC_FEATURE_COUNT]; unsigned long found_caps; }; @@ -665,6 +666,36 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return 0; } +int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, + struct intel_vsec_device *vsec_dev) +{ + struct vsec_priv *priv; + + priv = pci_get_drvdata(vsec_dev->pcidev); + if (!priv) + return -EINVAL; + + priv->plat_info = *plat_info; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(intel_vsec_set_mapping, "INTEL_VSEC"); + +struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev) +{ + struct vsec_priv *priv; + + if (!pci_match_id(intel_vsec_pci_ids, pdev)) + return ERR_PTR(-EINVAL); + + priv = pci_get_drvdata(pdev); + if (!priv) + return ERR_PTR(-EINVAL); + + return &priv->plat_info; +} +EXPORT_SYMBOL_NS_GPL(intel_vsec_get_mapping, "INTEL_VSEC"); + /* DG1 info */ static struct intel_vsec_header dg1_header = { .length = 0x10, diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index cd78d0b2e623c..4bd0c6e7857c0 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -183,11 +183,23 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device #if IS_ENABLED(CONFIG_INTEL_VSEC) int intel_vsec_register(struct pci_dev *pdev, struct intel_vsec_platform_info *info); +int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, + struct intel_vsec_device *vsec_dev); +struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); #else static inline int intel_vsec_register(struct pci_dev *pdev, struct intel_vsec_platform_info *info) { return -ENODEV; } +static inline int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, + struct intel_vsec_device *vsec_dev) +{ + return -ENODEV; +} +static inline struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev) +{ + return ERR_PTR(-ENODEV); +} #endif #endif -- GitLab From c9699057521834862616ce159a47bd33920f0d9f Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:27 -0700 Subject: [PATCH 395/868] platform/x86/intel/tpmi: Get OOBMSM CPU mapping from TPMI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy TPMI’s OOBMSM platform info into a common area within VSEC private data via intel_vsec_set_mapping(). This enables other Intel VSEC features to access the CPU mapping without additional queries. Additionally, designate the TPMI driver as a supplier for the Telemetry driver, ensuring it can obtain the necessary platform information for future feature extensions. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-13-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 2 +- drivers/platform/x86/intel/vsec_tpmi.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 711ff4edfe210..f66f0ce8559b1 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -725,7 +725,7 @@ static const struct intel_vsec_platform_info mtl_info = { static const struct vsec_feature_dependency oobmsm_deps[] = { { .feature = VSEC_CAP_TELEMETRY, - .supplier_bitmap = VSEC_CAP_DISCOVERY, + .supplier_bitmap = VSEC_CAP_DISCOVERY | VSEC_CAP_TPMI, }, }; diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index d95a0d994546f..7748b5557a180 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -799,6 +799,10 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) ret = tpmi_process_info(tpmi_info, pfs); if (ret) return ret; + + ret = intel_vsec_set_mapping(&tpmi_info->plat_info, vsec_dev); + if (ret) + return ret; } if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID) -- GitLab From 86fc85c75bcd9b0f28afadd60c9f890669b42ba4 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:28 -0700 Subject: [PATCH 396/868] platform/x86/intel/pmt/discovery: Get telemetry attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add intel_pmt_get_features() in PMT Discovery to enable the PMT Telemetry driver to obtain attributes of the aggregated telemetry spaces it enumerates. The function gathers feature flags and associated data (like the number of RMIDs) from each PMT entry, laying the groundwork for a future kernel interface that will allow direct access to telemetry regions based on their capabilities. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-14-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/Kconfig | 1 + drivers/platform/x86/intel/pmt/class.h | 7 +++++ drivers/platform/x86/intel/pmt/discovery.c | 33 ++++++++++++++++++++++ drivers/platform/x86/intel/pmt/telemetry.c | 5 ++++ include/linux/intel_vsec.h | 16 +++++++++++ 5 files changed, 62 insertions(+) diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index 0ad91b5112e9d..83ae17eab4623 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -18,6 +18,7 @@ config INTEL_PMT_CLASS config INTEL_PMT_TELEMETRY tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" depends on INTEL_VSEC + select INTEL_PMT_DISCOVERY select INTEL_PMT_CLASS help The Intel Platform Monitory Technology (PMT) Telemetry driver provides diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 39c32357ee2c9..fdf7e79d8c0dc 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -48,6 +48,7 @@ struct intel_pmt_entry { struct pmt_callbacks *cb; unsigned long base_addr; size_t size; + u64 feature_flags; u32 guid; u32 num_rmids; /* Number of Resource Monitoring IDs */ int devid; @@ -71,4 +72,10 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_vsec_device *dev, int idx); void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns); +#if IS_ENABLED(CONFIG_INTEL_PMT_DISCOVERY) +void intel_pmt_get_features(struct intel_pmt_entry *entry); +#else +static inline void intel_pmt_get_features(struct intel_pmt_entry *entry) {} +#endif + #endif diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index 4b4fa3137ad2e..e72d43b675b46 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -583,6 +583,39 @@ abort_probe: return ret; } +static void pmt_get_features(struct intel_pmt_entry *entry, struct feature *f) +{ + int num_guids = f->table.header.num_guids; + int i; + + for (i = 0; i < num_guids; i++) { + if (f->table.guids[i] != entry->guid) + continue; + + entry->feature_flags |= BIT(f->id); + + if (feature_layout[f->id] == LAYOUT_RMID) + entry->num_rmids = f->table.rmid.num_rmids; + else + entry->num_rmids = 0; /* entry is kzalloc but set anyway */ + } +} + +void intel_pmt_get_features(struct intel_pmt_entry *entry) +{ + struct feature *feature; + + mutex_lock(&feature_list_lock); + list_for_each_entry(feature, &pmt_feature_list, list) { + if (feature->priv->parent != &entry->ep->pcidev->dev) + continue; + + pmt_get_features(entry, feature); + } + mutex_unlock(&feature_list_lock); +} +EXPORT_SYMBOL_NS_GPL(intel_pmt_get_features, "INTEL_PMT"); + static const struct auxiliary_device_id pmt_features_id_table[] = { { .name = "intel_vsec.discovery" }, {} diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index ac3a9bdf56015..58d06749e4172 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -9,11 +9,14 @@ */ #include +#include #include #include +#include #include #include #include +#include #include #include @@ -311,6 +314,8 @@ static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxilia continue; priv->num_entries++; + + intel_pmt_get_features(entry); } return 0; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 4bd0c6e7857c0..f185e9c01c905 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -4,6 +4,7 @@ #include #include +#include /* * VSEC_CAP_UNUSED is reserved. It exists to prevent zero initialized @@ -166,6 +167,21 @@ struct oobmsm_plat_info { u8 function_number; }; +struct telemetry_region { + struct oobmsm_plat_info plat_info; + void __iomem *addr; + size_t size; + u32 guid; + u32 num_rmids; +}; + +struct pmt_feature_group { + enum pmt_feature_id id; + int count; + struct kref kref; + struct telemetry_region regions[]; +}; + int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name); -- GitLab From 42dabe5442887946b16e64c6ebe91d2671a96fbb Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:29 -0700 Subject: [PATCH 397/868] platform/x86/intel/pmt/telemetry: Add API to retrieve telemetry regions by feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new API, intel_pmt_get_regions_by_feature(), that gathers telemetry regions based on a provided capability flag. This API enables retrieval of regions with various capabilities (for example, RMID-based telemetry) and provides a unified interface for accessing them. Resource management is handled via reference counting using intel_pmt_put_feature_group(). Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-15-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/telemetry.c | 89 +++++++++++++++++++++- include/linux/intel_vsec.h | 18 +++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 58d06749e4172..a4dfca6cac199 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -9,16 +9,21 @@ */ #include +#include +#include +#include #include #include #include #include #include +#include +#include #include #include #include #include -#include +#include #include "class.h" @@ -209,6 +214,87 @@ unlock: } EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, "INTEL_PMT_TELEMETRY"); +static int pmt_copy_region(struct telemetry_region *region, + struct intel_pmt_entry *entry) +{ + + struct oobmsm_plat_info *plat_info; + + plat_info = intel_vsec_get_mapping(entry->ep->pcidev); + if (IS_ERR(plat_info)) + return PTR_ERR(plat_info); + + region->plat_info = *plat_info; + region->guid = entry->guid; + region->addr = entry->ep->base; + region->size = entry->size; + region->num_rmids = entry->num_rmids; + + return 0; +} + +static void pmt_feature_group_release(struct kref *kref) +{ + struct pmt_feature_group *feature_group; + + feature_group = container_of(kref, struct pmt_feature_group, kref); + kfree(feature_group); +} + +struct pmt_feature_group *intel_pmt_get_regions_by_feature(enum pmt_feature_id id) +{ + struct pmt_feature_group *feature_group __free(kfree) = NULL; + struct telemetry_region *region; + struct intel_pmt_entry *entry; + unsigned long idx; + int count = 0; + size_t size; + + if (!pmt_feature_id_is_valid(id)) + return ERR_PTR(-EINVAL); + + guard(mutex)(&ep_lock); + xa_for_each(&telem_array, idx, entry) { + if (entry->feature_flags & BIT(id)) + count++; + } + + if (!count) + return ERR_PTR(-ENOENT); + + size = struct_size(feature_group, regions, count); + feature_group = kzalloc(size, GFP_KERNEL); + if (!feature_group) + return ERR_PTR(-ENOMEM); + + feature_group->count = count; + + region = feature_group->regions; + xa_for_each(&telem_array, idx, entry) { + int ret; + + if (!(entry->feature_flags & BIT(id))) + continue; + + ret = pmt_copy_region(region, entry); + if (ret) + return ERR_PTR(ret); + + region++; + } + + kref_init(&feature_group->kref); + + return no_free_ptr(feature_group); +} +EXPORT_SYMBOL(intel_pmt_get_regions_by_feature); + +void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group) +{ + kref_put(&feature_group->kref, pmt_feature_group_release); +} +EXPORT_SYMBOL(intel_pmt_put_feature_group); + int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) { u32 offset, size; @@ -353,3 +439,4 @@ MODULE_AUTHOR("David E. Box "); MODULE_DESCRIPTION("Intel PMT Telemetry driver"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("INTEL_PMT"); +MODULE_IMPORT_NS("INTEL_VSEC"); diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index f185e9c01c905..53f6fe88e369e 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -4,6 +4,7 @@ #include #include +#include #include /* @@ -218,4 +219,21 @@ static inline struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pd return ERR_PTR(-ENODEV); } #endif + +#if IS_ENABLED(CONFIG_INTEL_PMT_TELEMETRY) +struct pmt_feature_group * +intel_pmt_get_regions_by_feature(enum pmt_feature_id id); + +void intel_pmt_put_feature_group(struct pmt_feature_group *feature_group); +#else +static inline struct pmt_feature_group * +intel_pmt_get_regions_by_feature(enum pmt_feature_id id) +{ + return ERR_PTR(-ENODEV); +} + +static inline void +intel_pmt_put_feature_group(struct pmt_feature_group *feature_group) {} +#endif + #endif -- GitLab From b9707d46a95962bb4e28ae1929015e419ad6aff7 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 2 Jul 2025 19:28:30 -0700 Subject: [PATCH 398/868] platform/x86/intel/pmt: KUNIT test for PMT Enhanced Discovery API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a KUNIT test for the intel_pmt_get_regions_by_feature() API. Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20250703022832.1302928-16-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/Kconfig | 14 +++ drivers/platform/x86/intel/pmt/Makefile | 2 + .../platform/x86/intel/pmt/discovery-kunit.c | 116 ++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 drivers/platform/x86/intel/pmt/discovery-kunit.c diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index 83ae17eab4623..785c206e1beb8 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -51,3 +51,17 @@ config INTEL_PMT_DISCOVERY To compile this driver as a module, choose M here: the module will be called pmt_discovery. + +config INTEL_PMT_KUNIT_TEST + tristate "KUnit tests for Intel PMT driver" + depends on INTEL_PMT_DISCOVERY + depends on KUNIT + help + Enable this option to compile and run a suite of KUnit tests for the Intel + Platform Monitoring Technology (PMT) driver. These tests are designed to + validate the driver's functionality, error handling, and overall stability, + helping developers catch regressions and ensure code quality during changes. + + This option is intended for development and testing environments. It is + recommended to disable it in production builds. To compile this driver as a + module, choose M here: the module will be called pmt-discovery-kunit. diff --git a/drivers/platform/x86/intel/pmt/Makefile b/drivers/platform/x86/intel/pmt/Makefile index 8aed7e1592e43..47f692c091c95 100644 --- a/drivers/platform/x86/intel/pmt/Makefile +++ b/drivers/platform/x86/intel/pmt/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_INTEL_PMT_CRASHLOG) += pmt_crashlog.o pmt_crashlog-y := crashlog.o obj-$(CONFIG_INTEL_PMT_DISCOVERY) += pmt_discovery.o pmt_discovery-y := discovery.o features.o +obj-$(CONFIG_INTEL_PMT_KUNIT_TEST) += pmt-discovery-kunit.o +pmt-discovery-kunit-y := discovery-kunit.o diff --git a/drivers/platform/x86/intel/pmt/discovery-kunit.c b/drivers/platform/x86/intel/pmt/discovery-kunit.c new file mode 100644 index 0000000000000..b4493fb96738a --- /dev/null +++ b/drivers/platform/x86/intel/pmt/discovery-kunit.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Platform Monitory Technology Discovery KUNIT tests + * + * Copyright (c) 2025, Intel Corporation. + * All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define PMT_FEATURE_COUNT (FEATURE_MAX + 1) + +static void +validate_pmt_regions(struct kunit *test, struct pmt_feature_group *feature_group, int feature_id) +{ + int i; + + kunit_info(test, "Feature ID %d [%s] has %d regions.\n", feature_id, + pmt_feature_names[feature_id], feature_group->count); + + for (i = 0; i < feature_group->count; i++) { + struct telemetry_region *region = &feature_group->regions[i]; + + kunit_info(test, " - Region %d: cdie_mask=%u, package_id=%u, partition=%u, segment=%u,", + i, region->plat_info.cdie_mask, region->plat_info.package_id, + region->plat_info.partition, region->plat_info.segment); + kunit_info(test, "\t\tbus=%u, device=%u, function=%u, guid=0x%x,", + region->plat_info.bus_number, region->plat_info.device_number, + region->plat_info.function_number, region->guid); + kunit_info(test, "\t\taddr=%p, size=%lu, num_rmids=%u", region->addr, region->size, + region->num_rmids); + + + KUNIT_ASSERT_GE(test, region->plat_info.cdie_mask, 0); + KUNIT_ASSERT_GE(test, region->plat_info.package_id, 0); + KUNIT_ASSERT_GE(test, region->plat_info.partition, 0); + KUNIT_ASSERT_GE(test, region->plat_info.segment, 0); + KUNIT_ASSERT_GE(test, region->plat_info.bus_number, 0); + KUNIT_ASSERT_GE(test, region->plat_info.device_number, 0); + KUNIT_ASSERT_GE(test, region->plat_info.function_number, 0); + + KUNIT_ASSERT_NE(test, region->guid, 0); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, (__force const void *)region->addr); + } +} + +static void linebreak(struct kunit *test) +{ + kunit_info(test, "*****************************************************************************\n"); +} + +static void test_intel_pmt_get_regions_by_feature(struct kunit *test) +{ + struct pmt_feature_group *feature_group; + int num_available = 0; + int feature_id; + + /* Iterate through all possible feature IDs */ + for (feature_id = 1; feature_id < PMT_FEATURE_COUNT; feature_id++, linebreak(test)) { + const char *name; + + if (!pmt_feature_id_is_valid(feature_id)) + continue; + + name = pmt_feature_names[feature_id]; + + feature_group = intel_pmt_get_regions_by_feature(feature_id); + if (IS_ERR(feature_group)) { + if (PTR_ERR(feature_group) == -ENOENT) + kunit_warn(test, "intel_pmt_get_regions_by_feature() reporting feature %d [%s] is not present.\n", + feature_id, name); + else + kunit_warn(test, "intel_pmt_get_regions_by_feature() returned error %ld while attempt to lookup %d [%s].\n", + PTR_ERR(feature_group), feature_id, name); + + continue; + } + + if (!feature_group) { + kunit_warn(test, "Feature ID %d: %s is not available.\n", feature_id, name); + continue; + } + + num_available++; + + validate_pmt_regions(test, feature_group, feature_id); + + intel_pmt_put_feature_group(feature_group); + } + + if (num_available == 0) + kunit_warn(test, "No PMT region groups were available for any feature ID (0-10).\n"); +} + +static struct kunit_case intel_pmt_discovery_test_cases[] = { + KUNIT_CASE(test_intel_pmt_get_regions_by_feature), + {} +}; + +static struct kunit_suite intel_pmt_discovery_test_suite = { + .name = "pmt_discovery_test", + .test_cases = intel_pmt_discovery_test_cases, +}; + +kunit_test_suite(intel_pmt_discovery_test_suite); + +MODULE_IMPORT_NS("INTEL_PMT_DISCOVERY"); +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMT Discovery KUNIT test driver"); +MODULE_LICENSE("GPL"); -- GitLab From cfbbf275ffcf05c82994b8787b0d1974aa1569d8 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Wed, 2 Jul 2025 13:22:51 -0500 Subject: [PATCH 399/868] gpio: palmas: Allow building as a module The driver works fine as a module, so allowing building as such. This adds an exit handler to support module unload. Signed-off-by: Aaron Kling Link: https://lore.kernel.org/r/20250702-gpio-palmas-gpio-v4-1-26ba48252f27@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- drivers/gpio/gpio-palmas.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b552401e3f73f..6802e549621b2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1502,7 +1502,7 @@ config GPIO_MAX77759 called gpio-max77759. config GPIO_PALMAS - bool "TI PALMAS series PMICs GPIO" + tristate "TI PALMAS series PMICs GPIO" depends on MFD_PALMAS help Select this option to enable GPIO driver for the TI PALMAS diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index a076daee00658..9329d8ce8f598 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -139,6 +139,7 @@ static const struct of_device_id of_palmas_gpio_match[] = { { .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,}, { }, }; +MODULE_DEVICE_TABLE(of, of_palmas_gpio_match); static int palmas_gpio_probe(struct platform_device *pdev) { @@ -196,3 +197,13 @@ static int __init palmas_gpio_init(void) return platform_driver_register(&palmas_gpio_driver); } subsys_initcall(palmas_gpio_init); + +static void __exit palmas_gpio_exit(void) +{ + platform_driver_unregister(&palmas_gpio_driver); +} +module_exit(palmas_gpio_exit); + +MODULE_DESCRIPTION("TI PALMAS series GPIO driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL"); -- GitLab From 7105fdd54a14bee49371b39374a61b3c967d74cb Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 2 Jul 2025 17:26:42 -0500 Subject: [PATCH 400/868] spi: dt-bindings: Convert marvell,orion-spi to DT schema Convert the Marvell Orion SPI binding to schema. Update compatible strings to what is in use. Generally, "marvell,orion-spi" is a fallback compatible, but newer variants only use "marvell,armada-380-spi". Mark cell-index as deprecated and not required as some instances don't use it already. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20250702222643.2761617-1-robh@kernel.org Signed-off-by: Mark Brown --- .../bindings/spi/marvell,orion-spi.yaml | 102 ++++++++++++++++++ .../devicetree/bindings/spi/spi-orion.txt | 79 -------------- 2 files changed, 102 insertions(+), 79 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml delete mode 100644 Documentation/devicetree/bindings/spi/spi-orion.txt diff --git a/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml new file mode 100644 index 0000000000000..7f5ec1d7f59b1 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/marvell,orion-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Orion SPI controller + +maintainers: + - Andrew Lunn + - Gregory CLEMENT + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + oneOf: + - enum: + - marvell,orion-spi + - marvell,armada-380-spi # For ap80x and cp11x + - items: + - enum: + - marvell,armada-370-spi + - marvell,armada-375-spi + - marvell,armada-380-spi + - marvell,armada-390-spi + - marvell,armada-xp-spi + - const: marvell,orion-spi + + cell-index: + description: Instance id for the SPI controller + deprecated: true + + reg: + minItems: 1 + items: + - description: control registers + - description: CS0 MBUS target/attribute registers for direct mode + - description: CS1 MBUS target/attribute registers for direct mode + - description: CS2 MBUS target/attribute registers for direct mode + - description: CS3 MBUS target/attribute registers for direct mode + - description: CS4 MBUS target/attribute registers for direct mode + - description: CS5 MBUS target/attribute registers for direct mode + - description: CS6 MBUS target/attribute registers for direct mode + - description: CS7 MBUS target/attribute registers for direct mode + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + items: + - const: core + - const: axi + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = <0x10600 0x28>; + clocks = <&coreclk 0>; + interrupts = <23>; + }; + - | + #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + + bus { + #address-cells = <2>; + #size-cells = <1>; + + spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = , /* control */ + , /* CS0 */ + , /* CS1 */ + , /* CS2 */ + , /* CS3 */ + , /* CS4 */ + , /* CS5 */ + , /* CS6 */ + ; /* CS7 */ + clocks = <&coreclk 0>; + interrupts = <23>; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt deleted file mode 100644 index 8434a65fc12a6..0000000000000 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ /dev/null @@ -1,79 +0,0 @@ -Marvell Orion SPI device - -Required properties: -- compatible : should be on of the following: - - "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs - - "marvell,armada-370-spi", for the Armada 370 SoCs - - "marvell,armada-375-spi", for the Armada 375 SoCs - - "marvell,armada-380-spi", for the Armada 38x SoCs - - "marvell,armada-390-spi", for the Armada 39x SoCs - - "marvell,armada-xp-spi", for the Armada XP SoCs -- reg : offset and length of the register set for the device. - This property can optionally have additional entries to configure - the SPI direct access mode that some of the Marvell SoCs support - additionally to the normal indirect access (PIO) mode. The values - for the MBus "target" and "attribute" are defined in the Marvell - SoC "Functional Specifications" Manual in the chapter "Marvell - Core Processor Address Decoding". - The eight register sets following the control registers refer to - chip-select lines 0 through 7 respectively. -- cell-index : Which of multiple SPI controllers is this. -- clocks : pointers to the reference clocks for this device, the first - one is the one used for the clock on the spi bus, the - second one is optional and is the clock used for the - functional part of the controller - -Optional properties: -- interrupts : Is currently not used. -- clock-names : names of used clocks, mandatory if the second clock is - used, the name must be "core", and "axi" (the latter - is only for Armada 7K/8K). - - -Example: - spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = <0x10600 0x28>; - interrupts = <23>; - }; - -Example with SPI direct mode support (optionally): - spi0: spi@10600 { - compatible = "marvell,orion-spi"; - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - reg = , /* control */ - , /* CS0 */ - , /* CS1 */ - , /* CS2 */ - , /* CS3 */ - , /* CS4 */ - , /* CS5 */ - , /* CS6 */ - ; /* CS7 */ - interrupts = <23>; - }; - -To enable the direct mode, the board specific 'ranges' property in the -'soc' node needs to add the entries for the desired SPI controllers -and its chip-selects that are used in the direct mode instead of PIO -mode. Here an example for this (SPI controller 0, device 1 and SPI -controller 1, device 2 are used in direct mode. All other SPI device -are used in the default indirect (PIO) mode): - soc { - /* - * Enable the SPI direct access by configuring an entry - * here in the board-specific ranges property - */ - ranges = , /* internal regs */ - , /* BootROM */ - , /* SPI0-DEV1 */ - ; /* SPI1-DEV2 */ - -For further information on the MBus bindings, please see the MBus -DT documentation: -Documentation/devicetree/bindings/bus/mvebu-mbus.txt -- GitLab From 9931d2899eec3737f4e4fa9fc900be7329816e94 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 20 Jun 2025 13:52:28 +0800 Subject: [PATCH 401/868] ASoC: fsl_mqs: Distinguish different modules by system manager indices On i.MX94, the MQS2 also needs to be configured by SCMI interface, add sm_index variable in struct fsl_mqs_soc_data to distinguish the MQS1 and MQS2 on this platform. Add the system manager indices for i.MX94 in the header file. Signed-off-by: Shengjiu Wang Reviewed-by: Peng Fan Link: https://patch.msgid.link/20250620055229.965942-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- include/linux/firmware/imx/sm.h | 8 ++++++++ sound/soc/fsl/fsl_mqs.c | 11 ++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/linux/firmware/imx/sm.h b/include/linux/firmware/imx/sm.h index a8a17eeb7d907..a6220c500f7c1 100644 --- a/include/linux/firmware/imx/sm.h +++ b/include/linux/firmware/imx/sm.h @@ -18,6 +18,14 @@ #define SCMI_IMX_CTRL_SAI4_MCLK 4 /* WAKE SAI4 MCLK */ #define SCMI_IMX_CTRL_SAI5_MCLK 5 /* WAKE SAI5 MCLK */ +#define SCMI_IMX94_CTRL_PDM_CLK_SEL 0U /*!< AON PDM clock sel */ +#define SCMI_IMX94_CTRL_MQS1_SETTINGS 1U /*!< AON MQS settings */ +#define SCMI_IMX94_CTRL_MQS2_SETTINGS 2U /*!< WAKE MQS settings */ +#define SCMI_IMX94_CTRL_SAI1_MCLK 3U /*!< AON SAI1 MCLK */ +#define SCMI_IMX94_CTRL_SAI2_MCLK 4U /*!< WAKE SAI2 MCLK */ +#define SCMI_IMX94_CTRL_SAI3_MCLK 5U /*!< WAKE SAI3 MCLK */ +#define SCMI_IMX94_CTRL_SAI4_MCLK 6U /*!< WAKE SAI4 MCLK */ + int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val); int scmi_imx_misc_ctrl_set(u32 id, u32 val); diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index e34e5ea98de52..11f2f3792dcee 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -39,6 +39,7 @@ enum reg_type { * struct fsl_mqs_soc_data - soc specific data * * @type: control register space type + * @sm_index: index from definition in system manager * @ctrl_off: control register offset * @en_mask: enable bit mask * @en_shift: enable bit shift @@ -51,6 +52,7 @@ enum reg_type { */ struct fsl_mqs_soc_data { enum reg_type type; + int sm_index; int ctrl_off; int en_mask; int en_shift; @@ -82,7 +84,7 @@ static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val) if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) && mqs_priv->soc->ctrl_off == reg) - return scmi_imx_misc_ctrl_get(SCMI_IMX_CTRL_MQS1_SETTINGS, &num, val); + return scmi_imx_misc_ctrl_get(mqs_priv->soc->sm_index, &num, val); return -EINVAL; }; @@ -93,7 +95,7 @@ static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val) if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) && mqs_priv->soc->ctrl_off == reg) - return scmi_imx_misc_ctrl_set(SCMI_IMX_CTRL_MQS1_SETTINGS, val); + return scmi_imx_misc_ctrl_set(mqs_priv->soc->sm_index, val); return -EINVAL; }; @@ -386,6 +388,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = { .type = TYPE_REG_SM, + .sm_index = SCMI_IMX_CTRL_MQS1_SETTINGS, .ctrl_off = 0x88, .en_mask = BIT(1), .en_shift = 1, @@ -412,6 +415,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = { static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = { .type = TYPE_REG_SM, + .sm_index = SCMI_IMX94_CTRL_MQS1_SETTINGS, .ctrl_off = 0x88, .en_mask = BIT(1), .en_shift = 1, @@ -424,7 +428,8 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = { }; static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = { - .type = TYPE_REG_GPR, + .type = TYPE_REG_SM, + .sm_index = SCMI_IMX94_CTRL_MQS2_SETTINGS, .ctrl_off = 0x10, .en_mask = BIT(1), .en_shift = 1, -- GitLab From baee26a9d6cd3d3c6c3c03c56270aa647a67e4bd Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 20 Jun 2025 13:52:29 +0800 Subject: [PATCH 402/868] ASoC: fsl_mqs: rename system manager indices for i.MX95 The system manager indices names are different for each platform, rename the indices for i.MX95 to differentiate with other platform. Signed-off-by: Shengjiu Wang Reviewed-by: Peng Fan Link: https://patch.msgid.link/20250620055229.965942-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- include/linux/firmware/imx/sm.h | 12 ++++++------ sound/soc/fsl/fsl_mqs.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/firmware/imx/sm.h b/include/linux/firmware/imx/sm.h index a6220c500f7c1..d4212bc42b2c1 100644 --- a/include/linux/firmware/imx/sm.h +++ b/include/linux/firmware/imx/sm.h @@ -11,12 +11,12 @@ #include #include -#define SCMI_IMX_CTRL_PDM_CLK_SEL 0 /* AON PDM clock sel */ -#define SCMI_IMX_CTRL_MQS1_SETTINGS 1 /* AON MQS settings */ -#define SCMI_IMX_CTRL_SAI1_MCLK 2 /* AON SAI1 MCLK */ -#define SCMI_IMX_CTRL_SAI3_MCLK 3 /* WAKE SAI3 MCLK */ -#define SCMI_IMX_CTRL_SAI4_MCLK 4 /* WAKE SAI4 MCLK */ -#define SCMI_IMX_CTRL_SAI5_MCLK 5 /* WAKE SAI5 MCLK */ +#define SCMI_IMX95_CTRL_PDM_CLK_SEL 0 /* AON PDM clock sel */ +#define SCMI_IMX95_CTRL_MQS1_SETTINGS 1 /* AON MQS settings */ +#define SCMI_IMX95_CTRL_SAI1_MCLK 2 /* AON SAI1 MCLK */ +#define SCMI_IMX95_CTRL_SAI3_MCLK 3 /* WAKE SAI3 MCLK */ +#define SCMI_IMX95_CTRL_SAI4_MCLK 4 /* WAKE SAI4 MCLK */ +#define SCMI_IMX95_CTRL_SAI5_MCLK 5 /* WAKE SAI5 MCLK */ #define SCMI_IMX94_CTRL_PDM_CLK_SEL 0U /*!< AON PDM clock sel */ #define SCMI_IMX94_CTRL_MQS1_SETTINGS 1U /*!< AON MQS settings */ diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 11f2f3792dcee..901f840df9047 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -388,7 +388,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = { static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = { .type = TYPE_REG_SM, - .sm_index = SCMI_IMX_CTRL_MQS1_SETTINGS, + .sm_index = SCMI_IMX95_CTRL_MQS1_SETTINGS, .ctrl_off = 0x88, .en_mask = BIT(1), .en_shift = 1, -- GitLab From 4734c8b46b901cff2feda8b82abc710b65dc31c1 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 2 Jul 2025 08:39:51 -0700 Subject: [PATCH 403/868] ACPI: APEI: GHES: add TAINT_MACHINE_CHECK on GHES panic path When a GHES (Generic Hardware Error Source) triggers a panic, add the TAINT_MACHINE_CHECK taint flag to the kernel. This explicitly marks the kernel as tainted due to a machine check event, improving diagnostics and post-mortem analysis. The taint is set with LOCKDEP_STILL_OK to indicate lockdep remains valid. At large scale deployment, this helps to quickly determine panics that are coming due to hardware failures. Signed-off-by: Breno Leitao Reviewed-by: Tony Luck Link: https://patch.msgid.link/20250702-add_tain-v1-1-9187b10914b9@debian.org Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index f0584ccad4519..3d44f926afe8e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1088,6 +1088,8 @@ static void __ghes_panic(struct ghes *ghes, __ghes_print_estatus(KERN_EMERG, ghes->generic, estatus); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK); + ghes_clear_estatus(ghes, estatus, buf_paddr, fixmap_idx); if (!panic_timeout) -- GitLab From 13edf7539211d8f7d0068ce3ed143005f1da3547 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 3 Jul 2025 14:42:15 +0200 Subject: [PATCH 404/868] ACPI: processor: fix acpi_object initialization Initialization of the local acpi_object in acpi_processor_get_info() only sets the first 4 bytes to zero and is thus incomplete. This is indicated by messages like: acpi ACPI0007:be: Invalid PBLK length [166288104] Fix this by initializing all 16 bytes of the processor member of that union. Signed-off-by: Sebastian Ott Link: https://patch.msgid.link/20250703124215.12522-1-sebott@redhat.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_processor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 7cf6101cb4c73..2a99f5eb69629 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -275,7 +275,7 @@ static inline int acpi_processor_hotadd_init(struct acpi_processor *pr, static int acpi_processor_get_info(struct acpi_device *device) { - union acpi_object object = { 0 }; + union acpi_object object = { .processor = { 0 } }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); int device_declaration = 0; -- GitLab From 1a4aabc27e95674837f2e25f4ef340c0469e6203 Mon Sep 17 00:00:00 2001 From: Hsin-Te Yuan Date: Fri, 20 Jun 2025 10:41:43 +0000 Subject: [PATCH 405/868] thermal: sysfs: Return ENODATA instead of EAGAIN for reads According to POSIX spec, EAGAIN returned by read with O_NONBLOCK set means the read would block. Hence, the common implementation in nonblocking model will poll the file when the nonblocking read returns EAGAIN. However, when the target file is thermal zone, this mechanism will totally malfunction because thermal zone doesn't implement sysfs notification and thus the poll will never return. For example, the read in Golang implemnts such method and sometimes hangs at reading some thermal zones via sysfs. Change to return -ENODATA instead of -EAGAIN to userspace. Signed-off-by: Hsin-Te Yuan Link: https://patch.msgid.link/20250620-temp-v3-1-6becc6aeb66c@chromium.org Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_sysfs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 24b9055a0b6c5..d80612506a334 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -40,10 +40,13 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf) ret = thermal_zone_get_temp(tz, &temperature); - if (ret) - return ret; + if (!ret) + return sprintf(buf, "%d\n", temperature); - return sprintf(buf, "%d\n", temperature); + if (ret == -EAGAIN) + return -ENODATA; + + return ret; } static ssize_t -- GitLab From 72a600a27ff53aaf94c81d5ddb9795139b56881e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 3 Jul 2025 15:42:52 +0200 Subject: [PATCH 406/868] ALSA: mtpav: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250703134255.3948-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/drivers/mtpav.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 851f34e2cdd08..91828f496738f 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -46,6 +46,7 @@ #include #include #include +#include /* * globals @@ -605,11 +606,11 @@ static void snd_mtpav_set_name(struct mtpav *chip, else if (substream->number >= 8 && substream->number < chip->num_ports * 2) sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1); else if (substream->number == chip->num_ports * 2) - strcpy(substream->name, "MTP computer"); + strscpy(substream->name, "MTP computer"); else if (substream->number == chip->num_ports * 2 + 1) - strcpy(substream->name, "MTP ADAT"); + strscpy(substream->name, "MTP ADAT"); else - strcpy(substream->name, "MTP broadcast"); + strscpy(substream->name, "MTP broadcast"); } static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard) @@ -697,8 +698,8 @@ static int snd_mtpav_probe(struct platform_device *dev) if (err < 0) return err; - strcpy(card->driver, "MTPAV"); - strcpy(card->shortname, "MTPAV on parallel port"); + strscpy(card->driver, "MTPAV"); + strscpy(card->shortname, "MTPAV on parallel port"); snprintf(card->longname, sizeof(card->longname), "MTPAV on parallel port at 0x%lx", port); -- GitLab From a48d994ca321caaa82cb657eceae7722416a0c07 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 4 Jul 2025 00:52:36 +0200 Subject: [PATCH 407/868] ALSA: hda: Remove old commented out sanity check The sanity check has been commented out for more than 12 years since commit d5657ec9f4ad ("ALSA: hda - Disable the sanity check in snd_hda_add_pincfg()") - remove it. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250703225238.308359-2-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5508381a18332..cb72e9655c8a6 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -479,15 +479,6 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, { struct hda_pincfg *pin; - /* the check below may be invalid when pins are added by a fixup - * dynamically (e.g. via snd_hda_codec_update_widgets()), so disabled - * for now - */ - /* - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) - return -EINVAL; - */ - pin = look_up_pincfg(codec, list, nid); if (!pin) { pin = snd_array_new(list); -- GitLab From 0d3d3d01947b842d3c8934394f5210143a54db4a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 4 Jul 2025 09:11:06 +0200 Subject: [PATCH 408/868] ALSA: hda: Add device entry for QEMU QEMU HD-audio device (1af4:0021) is handled by the generic HD-audio codec driver, hence it's better to have an explicit device ID listing, so that we can avoid the superfluous vendor driver matching. Link: https://patch.msgid.link/20250704071107.14626-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b34d84fedcc8a..2a28c8b6ba552 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -6144,6 +6144,7 @@ error: } static const struct hda_device_id snd_hda_id_generic[] = { + HDA_CODEC_ENTRY(0x1af40021, "Generic", snd_hda_parse_generic_codec), /* QEMU */ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), {} /* terminator */ }; -- GitLab From bc163baef57002c08b3afe64cdd2f55f55a765eb Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Thu, 3 Jul 2025 13:35:21 -0500 Subject: [PATCH 409/868] ASoC: Use of_reserved_mem_region_to_resource() for "memory-region" Use the newly added of_reserved_mem_region_to_resource() function to handle "memory-region" properties. Signed-off-by: Rob Herring (Arm) Reviewed-by: Tzung-Bi Shih Reviewed-by: Cheng-Yi Chiang Link: https://patch.msgid.link/20250703183523.2075276-1-robh@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/cros_ec_codec.c | 30 ++++++++++----------- sound/soc/sof/imx/imx-common.c | 36 +++++--------------------- sound/soc/sof/mediatek/mt8186/mt8186.c | 11 +------- sound/soc/sof/mediatek/mt8195/mt8195.c | 11 +------- 4 files changed, 22 insertions(+), 66 deletions(-) diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 571222ec520ca..937c8cec682ac 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -961,7 +962,6 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev) struct ec_response_ec_codec_get_capabilities r; int ret; #ifdef CONFIG_OF - struct device_node *node; struct resource res; u64 ec_shm_size; const __be32 *regaddr_p; @@ -981,22 +981,18 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev) priv->ec_shm_addr, priv->ec_shm_len); } - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (node) { - ret = of_address_to_resource(node, 0, &res); - if (!ret) { - priv->ap_shm_phys_addr = res.start; - priv->ap_shm_len = resource_size(&res); - priv->ap_shm_addr = - (uint64_t)(uintptr_t)devm_ioremap_wc( - dev, priv->ap_shm_phys_addr, - priv->ap_shm_len); - priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; - - dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", - priv->ap_shm_phys_addr, priv->ap_shm_len); - } - of_node_put(node); + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); + if (!ret) { + priv->ap_shm_phys_addr = res.start; + priv->ap_shm_len = resource_size(&res); + priv->ap_shm_addr = + (uint64_t)(uintptr_t)devm_ioremap_wc( + dev, priv->ap_shm_phys_addr, + priv->ap_shm_len); + priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; + + dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", + priv->ap_shm_phys_addr, priv->ap_shm_len); } #endif diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 62bf707aa909c..f00b381cec3b4 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -282,11 +282,8 @@ static int imx_region_name_to_blk_type(const char *region_name) static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) { const struct imx_chip_info *chip_info; - struct reserved_mem *reserved; struct platform_device *pdev; - struct device_node *res_np; - phys_addr_t base, size; - struct resource *res; + struct resource *res, _res; int i, blk_type, ret; pdev = to_platform_device(sdev->dev); @@ -307,37 +304,18 @@ static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) "failed to fetch %s resource\n", chip_info->memory[i].name); - base = res->start; - size = resource_size(res); } else { - ret = of_property_match_string(pdev->dev.of_node, - "memory-region-names", - chip_info->memory[i].name); + ret = of_reserved_mem_region_to_resource_byname(pdev->dev.of_node, + chip_info->memory[i].name, + &_res); if (ret < 0) return dev_err_probe(sdev->dev, ret, - "no valid index for %s\n", + "no valid entry for %s\n", chip_info->memory[i].name); - - res_np = of_parse_phandle(pdev->dev.of_node, - "memory-region", - ret); - if (!res_np) - return dev_err_probe(sdev->dev, -ENODEV, - "failed to parse phandle %s\n", - chip_info->memory[i].name); - - reserved = of_reserved_mem_lookup(res_np); - of_node_put(res_np); - if (!reserved) - return dev_err_probe(sdev->dev, -ENODEV, - "failed to get %s reserved\n", - chip_info->memory[i].name); - - base = reserved->base; - size = reserved->size; + res = &_res; } - sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size); + sdev->bar[blk_type] = devm_ioremap_resource(sdev->dev, res); if (!sdev->bar[blk_type]) return dev_err_probe(sdev->dev, -ENOMEM, diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 7ff080452cbec..c1bea967737d5 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) { struct resource *mmio; struct resource res; - struct device_node *mem_region; struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; int ret; @@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) return ret; } - mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); - if (!mem_region) { - dev_err(dev, "no memory-region sysmem phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); + ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res); if (ret) { dev_err(dev, "of_address_to_resource sysmem failed\n"); return ret; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 3b3582d745103..4d6e9300a9c03 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) { struct resource *mmio; struct resource res; - struct device_node *mem_region; struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; int ret; @@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) return ret; } - mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); - if (!mem_region) { - dev_err(dev, "no memory-region sysmem phandle\n"); - return -ENODEV; - } - - ret = of_address_to_resource(mem_region, 0, &res); - of_node_put(mem_region); + ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res); if (ret) { dev_err(dev, "of_address_to_resource sysmem failed\n"); return ret; -- GitLab From c61e94e5e4e6bc50064119e6a779564d1d2ac0e7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:44 +0300 Subject: [PATCH 410/868] regulator: stm32-vrefbuf: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075444.3221445-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- drivers/regulator/stm32-vrefbuf.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c index a85ea94f06730..9e391206f09dd 100644 --- a/drivers/regulator/stm32-vrefbuf.c +++ b/drivers/regulator/stm32-vrefbuf.c @@ -67,7 +67,6 @@ static int stm32_vrefbuf_enable(struct regulator_dev *rdev) writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -87,7 +86,6 @@ static int stm32_vrefbuf_disable(struct regulator_dev *rdev) val &= ~STM32_ENVR; writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -104,7 +102,6 @@ static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -125,7 +122,6 @@ static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -144,7 +140,6 @@ static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ret = FIELD_GET(STM32_VRS, val); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -218,7 +213,6 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; -- GitLab From 9f711c9321cffe3e03709176873c277fa911c366 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 2 Jul 2025 22:16:02 +0100 Subject: [PATCH 411/868] regmap: get rid of redundant debugfs_file_{get,put}() pointless in ->read()/->write() of file_operations used only via debugfs_create_file() Signed-off-by: Al Viro Link: https://patch.msgid.link/20250702211602.GC3406663@ZenIV Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index fb84cda92a753..c9b4c04b1cf60 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -470,10 +470,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file, if (err) return count; - err = debugfs_file_get(file->f_path.dentry); - if (err) - return err; - map->lock(map->lock_arg); if (new_val && !map->cache_only) { @@ -486,7 +482,6 @@ static ssize_t regmap_cache_only_write_file(struct file *file, map->cache_only = new_val; map->unlock(map->lock_arg); - debugfs_file_put(file->f_path.dentry); if (require_sync) { err = regcache_sync(map); @@ -517,10 +512,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file, if (err) return count; - err = debugfs_file_get(file->f_path.dentry); - if (err) - return err; - map->lock(map->lock_arg); if (new_val && !map->cache_bypass) { @@ -532,7 +523,6 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file, map->cache_bypass = new_val; map->unlock(map->lock_arg); - debugfs_file_put(file->f_path.dentry); return count; } -- GitLab From 571defe0dff3f1e4180bd0db79283d3d5bf74a71 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 4 Jul 2025 21:09:06 +0800 Subject: [PATCH 412/868] ASoC: codec: rockchip_sai: Remove including of_gpio.h of_gpio.h is deprecated. And there is no user in this driver using API in of_gpio.h, so remove it. Signed-off-by: Peng Fan Acked-by: Nicolas Frattaroli Link: https://patch.msgid.link/20250704130906.1207134-1-peng.fan@nxp.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_sai.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 0b9f54102d693..6695349ee561e 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include -- GitLab From 9069141d1d9c585a20e43037c2f9c00d9d3fc9eb Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:55 +0300 Subject: [PATCH 413/868] ASoC: atmel: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075455.3222541-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdifrx.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index fb820609c043d..521bee4998f8d 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -577,7 +577,6 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, sizeof(ch_stat->data)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -660,7 +659,6 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, sizeof(user_data->data)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -726,7 +724,6 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol, uvalue->value.integer.value[0] = ctrl->ulock; - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -762,7 +759,6 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol, ctrl->badf = 0; } - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); @@ -811,7 +807,6 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol, regmap_read(dev->regmap, SPDIFRX_RSR, &val); } - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: @@ -875,7 +870,6 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val)); pm_runtime_put: - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); unlock: mutex_unlock(&dev->mlock); -- GitLab From bbe5e3c433a34e7f7bc762c390abb38205f821c5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:56 +0300 Subject: [PATCH 414/868] ASoC: codecs: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075456.3222642-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/arizona-jack.c | 2 -- sound/soc/codecs/cs35l41.c | 2 -- sound/soc/codecs/cs35l45.c | 1 - sound/soc/codecs/cs35l56-sdw.c | 1 - sound/soc/codecs/cs35l56.c | 1 - sound/soc/codecs/cs42l42.c | 1 - sound/soc/codecs/cs42l43-jack.c | 5 ----- sound/soc/codecs/cs42l43.c | 2 -- sound/soc/codecs/cs48l32.c | 1 - sound/soc/codecs/hda.c | 3 --- sound/soc/codecs/max98363.c | 1 - sound/soc/codecs/max98373-sdw.c | 1 - sound/soc/codecs/rt1017-sdca-sdw.c | 1 - sound/soc/codecs/rt1308-sdw.c | 1 - sound/soc/codecs/rt1316-sdw.c | 1 - sound/soc/codecs/rt1318-sdw.c | 1 - sound/soc/codecs/rt1320-sdw.c | 1 - sound/soc/codecs/rt5682-sdw.c | 1 - sound/soc/codecs/rt700.c | 2 -- sound/soc/codecs/rt711-sdca.c | 2 -- sound/soc/codecs/rt711.c | 2 -- sound/soc/codecs/rt712-sdca-dmic.c | 1 - sound/soc/codecs/rt712-sdca.c | 2 -- sound/soc/codecs/rt715-sdca.c | 1 - sound/soc/codecs/rt715.c | 1 - sound/soc/codecs/rt721-sdca.c | 2 -- sound/soc/codecs/rt722-sdca.c | 2 -- sound/soc/codecs/rt9123.c | 3 --- sound/soc/codecs/tas2552.c | 1 - sound/soc/codecs/wcd-mbhc-v2.c | 2 -- sound/soc/codecs/wsa881x.c | 1 - sound/soc/codecs/wsa883x.c | 1 - sound/soc/codecs/wsa884x.c | 1 - 33 files changed, 51 deletions(-) diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c index 9c15ddba6008c..22f9c431a0e55 100644 --- a/sound/soc/codecs/arizona-jack.c +++ b/sound/soc/codecs/arizona-jack.c @@ -319,7 +319,6 @@ static void arizona_stop_mic(struct arizona_priv *info) if (change) { regulator_disable(info->micvdd); - pm_runtime_mark_last_busy(arizona->dev); pm_runtime_put_autosuspend(arizona->dev); } } @@ -1127,7 +1126,6 @@ out: mutex_unlock(&info->lock); - pm_runtime_mark_last_busy(arizona->dev); pm_runtime_put_autosuspend(arizona->dev); return IRQ_HANDLED; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index ff4134bee858e..224d65987a8df 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -483,7 +483,6 @@ static irqreturn_t cs35l41_irq(int irq, void *data) } done: - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_put_autosuspend(cs35l41->dev); return ret; @@ -1328,7 +1327,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg * pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); pm_runtime_use_autosuspend(cs35l41->dev); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_set_active(cs35l41->dev); pm_runtime_get_noresume(cs35l41->dev); pm_runtime_enable(cs35l41->dev); diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index 432a19f4de2b4..d4dcdf37bb709 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -1427,7 +1427,6 @@ int cs35l45_probe(struct cs35l45_private *cs35l45) pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); pm_runtime_use_autosuspend(cs35l45->dev); - pm_runtime_mark_last_busy(cs35l45->dev); pm_runtime_set_active(cs35l45->dev); pm_runtime_get_noresume(cs35l45->dev); pm_runtime_enable(cs35l45->dev); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 13f602f51bf32..2c722a0ea8526 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -282,7 +282,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) } out: - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index c78e4746e4287..c6658dcc6dd1a 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -849,7 +849,6 @@ static void cs35l56_dsp_work(struct work_struct *work) cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp); err: - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 56668c3920639..78bb093fa0cc0 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1775,7 +1775,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data) } mutex_unlock(&cs42l42->irq_lock); - pm_runtime_mark_last_busy(cs42l42->dev); pm_runtime_put_autosuspend(cs42l42->dev); return IRQ_HANDLED; diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 6165ac16c3a95..f5c5150c25e52 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -242,7 +242,6 @@ done: error: mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -423,7 +422,6 @@ void cs42l43_button_press_work(struct work_struct *work) error: mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -462,7 +460,6 @@ void cs42l43_button_release_work(struct work_struct *work) mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -504,7 +501,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work) mutex_unlock(&priv->jack_lock); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -776,7 +772,6 @@ error: priv->suspend_jack_debounce = false; - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index ea84ac64c775e..d84ad8d434380 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -1088,7 +1088,6 @@ static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) ret ? "open" : "closed"); error: - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -2370,7 +2369,6 @@ static int cs42l43_codec_probe(struct platform_device *pdev) goto err_clk; } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c index 90a795230d276..6503f04cf8bb1 100644 --- a/sound/soc/codecs/cs48l32.c +++ b/sound/soc/codecs/cs48l32.c @@ -1385,7 +1385,6 @@ static irqreturn_t cs48l32_irq(int irq, void *data) result = IRQ_HANDLED; out: - pm_runtime_mark_last_busy(cs48l32_codec->core.dev); pm_runtime_put_autosuspend(cs48l32_codec->core.dev); return result; diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index dc7794c9ac44c..7e4df14814860 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -162,7 +162,6 @@ int hda_codec_probe_complete(struct hda_codec *codec) snd_hda_codec_register(codec); /* Complement pm_runtime_get_sync(bus) in probe */ - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); return ret; @@ -262,7 +261,6 @@ device_new_err: snd_hdac_ext_bus_link_put(bus, hlink); - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); return ret; } @@ -300,7 +298,6 @@ static void hda_codec_remove(struct snd_soc_component *component) * not be called due to early error, leaving bus uc unbalanced */ if (!was_registered) { - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); } diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 950105e5bffdc..575331e4bc469 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -188,7 +188,6 @@ static int max98363_io_init(struct sdw_slave *slave) max98363->hw_init = true; out: - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 6088278e6503d..143c0384ba0f8 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -458,7 +458,6 @@ static int max98373_io_init(struct sdw_slave *slave) max98373->first_hw_init = true; max98373->hw_init = true; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 88fc23a4999fb..a9c000876be8b 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -362,7 +362,6 @@ static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1017->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "hw_init complete\n"); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index ea708068f0e8e..b6c224832a431 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -291,7 +291,6 @@ _preset_ready_: /* Mark Slave initialization complete */ rt1308->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 960b6c4f5a668..01a9773988646 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -302,7 +302,6 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt1316->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 4eb636e0c9ed1..70db5450d6d29 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -434,7 +434,6 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave) rt1318->first_hw_init = true; rt1318->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 015cc710e6dc0..b13d7a99bf63a 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -763,7 +763,6 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave) rt1320->first_hw_init = true; rt1320->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index aa229894129bf..055bea0a4a3ba 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -474,7 +474,6 @@ reinit: rt5682->first_hw_init = true; err_nodev: - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 434b926f96c83..8f6d569c8f25d 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -338,7 +338,6 @@ static int rt700_set_jack_detect(struct snd_soc_component *component, rt700_jack_init(rt700); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1230,7 +1229,6 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt700->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index dd6ccf17afd43..16c3517792434 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -545,7 +545,6 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, rt711_sdca_jack_init(rt711); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1662,7 +1661,6 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt711->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 5446f9506a167..af3a49aee618a 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -480,7 +480,6 @@ static int rt711_set_jack_detect(struct snd_soc_component *component, rt711_jack_init(rt711); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1331,7 +1330,6 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt711->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 4d044dfa3136e..42f8f7b8bed08 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -236,7 +236,6 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt712->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 570c2af1245d6..5b298db5f0f61 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -479,7 +479,6 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, rt712_sdca_jack_init(rt712); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1925,7 +1924,6 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); suspend: - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 7fb02654c16bd..db7d43349d7da 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -1065,7 +1065,6 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt715->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 2cf4618520918..0fa445d88e232 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -1129,7 +1129,6 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt715->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); return 0; diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c index 1c9f32e405cf9..2949e3d590499 100644 --- a/sound/soc/codecs/rt721-sdca.c +++ b/sound/soc/codecs/rt721-sdca.c @@ -327,7 +327,6 @@ static int rt721_sdca_set_jack_detect(struct snd_soc_component *component, rt721_sdca_jack_init(rt721); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1533,7 +1532,6 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt721->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index ac9588284a955..333611490ae35 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -339,7 +339,6 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, rt722_sdca_jack_init(rt722); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1559,7 +1558,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) /* Mark Slave initialization complete */ rt722->hw_init = true; - pm_runtime_mark_last_busy(&slave->dev); pm_runtime_put_autosuspend(&slave->dev); dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c index 242e8c975a621..b162824526d6b 100644 --- a/sound/soc/codecs/rt9123.c +++ b/sound/soc/codecs/rt9123.c @@ -77,7 +77,6 @@ static int rt9123_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontro /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -140,7 +139,6 @@ static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele if (ret < 0) dev_err(dev, "Failed to get control (%d)\n", ret); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; } @@ -168,7 +166,6 @@ static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele if (ret < 0) dev_err(dev, "Failed to put control (%d)\n", ret); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; } diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index b56dd279d90ac..43449d7c25843 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -724,7 +724,6 @@ static int tas2552_probe(struct i2c_client *client) pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_mark_last_busy(&client->dev); pm_runtime_put_sync_autosuspend(&client->dev); dev_set_drvdata(&client->dev, data); diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 4b7c3d6080a10..26ebcdadeb7da 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -825,7 +825,6 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_unlock(&mbhc->lock); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return 0; @@ -1319,7 +1318,6 @@ exit: if (mbhc->mbhc_cb->hph_pull_down_ctrl) mbhc->mbhc_cb->hph_pull_down_ctrl(component, true); - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); } diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 6627d2da37220..11e9b97754d4f 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -775,7 +775,6 @@ static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, usleep_range(1000, 1010); } - pm_runtime_mark_last_busy(comp->dev); pm_runtime_put_autosuspend(comp->dev); return 1; diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index f04d99c66f332..12eaf21774f36 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1491,7 +1491,6 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) ret = -EAGAIN; } out: - pm_runtime_mark_last_busy(wsa883x->dev); pm_runtime_put_autosuspend(wsa883x->dev); return ret; diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index fd6ebc25fe894..415c9df9bb16c 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1941,7 +1941,6 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) } out: - pm_runtime_mark_last_busy(wsa884x->dev); pm_runtime_put_autosuspend(wsa884x->dev); return ret; -- GitLab From 077e700cd709b9a0334bd442a1a4090c9de0d152 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:57 +0300 Subject: [PATCH 415/868] ASoC: Intel: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075457.3222746-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pvt.c | 1 - sound/soc/intel/avs/core.c | 1 - sound/soc/intel/avs/debugfs.c | 2 -- sound/soc/intel/avs/ipc.c | 1 - sound/soc/intel/avs/pcm.c | 1 - sound/soc/intel/catpt/pcm.c | 6 ------ sound/soc/intel/catpt/sysfs.c | 1 - 7 files changed, 13 deletions(-) diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 5d08f7d803f9d..c01b29616ebcb 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -259,7 +259,6 @@ int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) { int ret; - pm_runtime_mark_last_busy(sst_drv->dev); ret = pm_runtime_put_autosuspend(sst_drv->dev); if (ret < 0) return ret; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index ec1b3f55cb5c9..7af3247536735 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -231,7 +231,6 @@ static void avs_hda_probe_work(struct work_struct *work) /* configure PM */ pm_runtime_set_autosuspend_delay(bus->dev, 2000); pm_runtime_use_autosuspend(bus->dev); - pm_runtime_mark_last_busy(bus->dev); pm_runtime_put_autosuspend(bus->dev); pm_runtime_allow(bus->dev); } diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index c625cf879f170..f508f215ecd2b 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -315,7 +315,6 @@ err_ipc: if (!adev->logged_resources) { avs_dsp_enable_d0ix(adev); err_d0ix: - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); } @@ -342,7 +341,6 @@ static int disable_logs(struct avs_dev *adev, u32 resource_mask) /* If that's the last resource, allow for D3. */ if (!adev->logged_resources) { avs_dsp_enable_d0ix(adev); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 0314f9d4ea5f4..6bfb9d1a1ca81 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -141,7 +141,6 @@ static void avs_dsp_recovery(struct avs_dev *adev) if (ret < 0) dev_err(adev->dev, "dsp reboot failed: %d\n", ret); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_enable(adev->dev); pm_request_autosuspend(adev->dev); diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index ccf90428126d8..3a22a8cb51858 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -979,7 +979,6 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp) if (!ret) ret = avs_module_info_init(adev, false); - pm_runtime_mark_last_busy(adev->dev); pm_runtime_put_autosuspend(adev->dev); return ret; diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 81a2f0339e055..46acb7fdc547d 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -673,7 +673,6 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, ret = catpt_ipc_set_device_format(cdev, &devfmt); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) @@ -871,7 +870,6 @@ static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); } - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return 0; @@ -892,7 +890,6 @@ static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol, ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id, ucontrol->value.integer.value); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return ret; @@ -927,7 +924,6 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); } - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); return 0; @@ -958,7 +954,6 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id, ucontrol->value.integer.value); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) @@ -1035,7 +1030,6 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c index 936ac9d503ff3..048253002ec87 100644 --- a/sound/soc/intel/catpt/sysfs.c +++ b/sound/soc/intel/catpt/sysfs.c @@ -21,7 +21,6 @@ static ssize_t fw_version_show(struct device *dev, ret = catpt_ipc_get_fw_version(cdev, &version); - pm_runtime_mark_last_busy(cdev->dev); pm_runtime_put_autosuspend(cdev->dev); if (ret) -- GitLab From e879f14d88c8ce4ecb647d80983f74b2fdd9f18b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:58 +0300 Subject: [PATCH 416/868] ASoC: component: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075458.3222817-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-component.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 25f5e543ae8d3..65c4950940244 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1278,7 +1278,6 @@ void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, if (rollback && !soc_component_mark_match(component, stream, pm)) continue; - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); /* remove marked stream */ -- GitLab From 2bd9648d5a8d329ca734ca2c273a80934867471e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:59 +0300 Subject: [PATCH 417/868] ASoC: SOF: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Acked-by: Peter Ujfalusi Link: https://patch.msgid.link/20250704075459.3222908-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 1 - sound/soc/sof/debug.c | 1 - sound/soc/sof/ipc3-dtrace.c | 1 - sound/soc/sof/ipc4-loader.c | 1 - sound/soc/sof/pcm.c | 1 - sound/soc/sof/sof-client-ipc-flood-test.c | 1 - sound/soc/sof/sof-client-ipc-kernel-injector.c | 1 - sound/soc/sof/sof-client-ipc-msg-injector.c | 1 - sound/soc/sof/sof-client-probes.c | 3 --- 9 files changed, 11 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 463d418e72001..a3fd1d523c094 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -196,7 +196,6 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get) ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size); - pm_runtime_mark_last_busy(scomp->dev); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d0ffa1d711450..b24943a65c89f 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -217,7 +217,6 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s } ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); - pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); if (ret < 0 || reply->rhdr.error < 0) { ret = min(ret, reply->rhdr.error); diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 744a91a150bc9..e5c8fec173c4f 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -172,7 +172,6 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, goto error; } ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size); - pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); error: diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index d2f534d65edf1..6bcdb495267d5 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -236,7 +236,6 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id, ret = ipc4_data->load_library(sdev, fw_lib, false); - pm_runtime_mark_last_busy(sdev->dev); err = pm_runtime_put_autosuspend(sdev->dev); if (err < 0) dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n", diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d584a72e6f52f..c51d61b18db9d 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -728,7 +728,6 @@ static int sof_pcm_probe(struct snd_soc_component *component) ret); pm_error: - pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); return ret; diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c index 11b6f7da28826..373f3a1253721 100644 --- a/sound/soc/sof/sof-client-ipc-flood-test.c +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -223,7 +223,6 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf ret = sof_debug_ipc_flood_test(cdev, flood_duration_test, ipc_duration_ms, ipc_count); - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c index 8b28c3dc920cb..249bd2d6c8d28 100644 --- a/sound/soc/sof/sof-client-ipc-kernel-injector.c +++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c @@ -65,7 +65,6 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); - pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); if (ret < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret); diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c index ba7ca1c5027ff..9c8a0fbfb8dfe 100644 --- a/sound/soc/sof/sof-client-ipc-msg-injector.c +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -137,7 +137,6 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev) if (ret) dev_err(dev, "IPC message send failed: %d\n", ret); - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index aff9ce9804295..663c0d3c314c6 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -238,7 +238,6 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, kfree(desc); pm_error: - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err); @@ -289,7 +288,6 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from, if (!ret) ret = count; - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); @@ -337,7 +335,6 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, if (!ret) ret = count; - pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); -- GitLab From 2fca750160f29015ab1109bb478537a4e415f7cd Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:47 +0300 Subject: [PATCH 418/868] spi: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075447.3221784-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 5 ----- drivers/spi/spi-cadence-quadspi.c | 2 -- drivers/spi/spi-cadence.c | 1 - drivers/spi/spi-fsl-espi.c | 2 -- drivers/spi/spi-fsl-lpspi.c | 2 -- drivers/spi/spi-imx.c | 3 --- drivers/spi/spi-mtk-nor.c | 1 - drivers/spi/spi-nxp-fspi.c | 1 - drivers/spi/spi-omap2-mcspi.c | 3 --- drivers/spi/spi-rockchip-sfc.c | 3 --- drivers/spi/spi-s3c64xx.c | 3 --- drivers/spi/spi-sprd.c | 1 - drivers/spi/spi-stm32-ospi.c | 7 ------- drivers/spi/spi-stm32-qspi.c | 7 ------- drivers/spi/spi-stm32.c | 2 -- drivers/spi/spi-ti-qspi.c | 2 -- drivers/spi/spi-zynqmp-gqspi.c | 1 - drivers/spi/spi.c | 3 --- 18 files changed, 49 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 2f67973242279..d730d7c6b527c 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -965,7 +965,6 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) err = aq->ops->transfer(mem, op, offset); pm_runtime_put: - pm_runtime_mark_last_busy(&aq->pdev->dev); pm_runtime_put_autosuspend(&aq->pdev->dev); return err; } @@ -1168,7 +1167,6 @@ static int atmel_qspi_setup(struct spi_device *spi) aq->scr |= QSPI_SCR_SCBR(scbr); atmel_qspi_write(aq->scr, aq, QSPI_SCR); - pm_runtime_mark_last_busy(ctrl->dev.parent); pm_runtime_put_autosuspend(ctrl->dev.parent); return 0; @@ -1230,7 +1228,6 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi) aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive); atmel_qspi_write(aq->mr, aq, QSPI_MR); - pm_runtime_mark_last_busy(ctrl->dev.parent); pm_runtime_put_autosuspend(ctrl->dev.parent); return 0; @@ -1448,7 +1445,6 @@ static int atmel_qspi_probe(struct platform_device *pdev) if (err) goto dma_release; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -1582,7 +1578,6 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev) atmel_qspi_write(aq->scr, aq, QSPI_SCR); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index fe0f122f07b02..420e4fc55b9af 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1469,7 +1469,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = cqspi_mem_process(mem, op); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); if (ret) @@ -1975,7 +1974,6 @@ static int cqspi_probe(struct platform_device *pdev) goto probe_setup_failed; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 9e56bde877680..5ae09b21d23a5 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -662,7 +662,6 @@ static int cdns_spi_probe(struct platform_device *pdev) /* Set to default valid value */ ctlr->max_speed_hz = xspi->clk_rate / 4; xspi->speed_hz = ctlr->max_speed_hz; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); } else { ctlr->mode_bits |= SPI_NO_CS; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6a73eaa34cf7c..f2f1d3298e6c0 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -513,7 +513,6 @@ static int fsl_espi_setup(struct spi_device *spi) fsl_espi_setup_transfer(spi, NULL); - pm_runtime_mark_last_busy(espi->dev); pm_runtime_put_autosuspend(espi->dev); return 0; @@ -726,7 +725,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, dev_info(dev, "irq = %u\n", irq); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 5e38184452344..67d4000c3cef5 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -233,7 +233,6 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) struct fsl_lpspi_data *fsl_lpspi = spi_controller_get_devdata(controller); - pm_runtime_mark_last_busy(fsl_lpspi->dev); pm_runtime_put_autosuspend(fsl_lpspi->dev); return 0; @@ -966,7 +965,6 @@ static int fsl_lpspi_probe(struct platform_device *pdev) goto free_dma; } - pm_runtime_mark_last_busy(fsl_lpspi->dev); pm_runtime_put_autosuspend(fsl_lpspi->dev); return 0; diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index c93d80a4d734e..155ddeb8fcd46 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1748,7 +1748,6 @@ spi_imx_prepare_message(struct spi_controller *controller, struct spi_message *m ret = spi_imx->devtype_data->prepare_message(spi_imx, msg); if (ret) { - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); } @@ -1760,7 +1759,6 @@ spi_imx_unprepare_message(struct spi_controller *controller, struct spi_message { struct spi_imx_data *spi_imx = spi_controller_get_devdata(controller); - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); return 0; } @@ -1933,7 +1931,6 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_register_controller; } - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); return ret; diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 85ab5ce96c4df..5cc4632e13d7a 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -918,7 +918,6 @@ static int mtk_nor_probe(struct platform_device *pdev) if (ret < 0) goto err_probe; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq); diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index e63c77e418231..c7d4827f1bf17 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -968,7 +968,6 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) /* Invalidate the data in the AHB buffer. */ nxp_fspi_invalid(f); - pm_runtime_mark_last_busy(f->dev); pm_runtime_put_autosuspend(f->dev); return err; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 70bb74b3bd9c3..6dc58a30804a1 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -272,7 +272,6 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) mcspi_write_chconf0(spi, l); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); } } @@ -1102,7 +1101,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) if (ret && initial_setup) omap2_mcspi_cleanup(spi); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); return ret; @@ -1379,7 +1377,6 @@ static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi) ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; omap2_mcspi_set_mode(ctlr); - pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); return 0; } diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index f3fe10eddb6ac..9eba5c0a60f23 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -565,7 +565,6 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op ret = rockchip_sfc_xfer_done(sfc, 100000); out: - pm_runtime_mark_last_busy(sfc->dev); pm_runtime_put_autosuspend(sfc->dev); return ret; @@ -712,7 +711,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev) if (ret) goto err_register; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -799,7 +797,6 @@ static int rockchip_sfc_resume(struct device *dev) rockchip_sfc_init(sfc); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 9c47f5741c5f7..b1567243ae196 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1045,14 +1045,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi) } } - pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); s3c64xx_spi_set_cs(spi, false); return 0; setup_exit: - pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); /* setup() returns with device de-selected */ s3c64xx_spi_set_cs(spi, false); @@ -1384,7 +1382,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n", mem_res, sdd->fifo_depth); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index ae794058b3815..ad75f5f0f2bfc 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -982,7 +982,6 @@ static int sprd_spi_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 4ab7e86f4bd51..2829535e5cd4f 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -547,7 +547,6 @@ static int stm32_ospi_poll_status(struct spi_mem *mem, ret = stm32_ospi_send(mem->spi, op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -571,7 +570,6 @@ static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = stm32_ospi_send(mem->spi, op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -628,7 +626,6 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc, ret = stm32_ospi_send(desc->mem->spi, &op); mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret ?: len; @@ -713,7 +710,6 @@ end_of_transfer: msg->status = ret; spi_finalize_current_message(ctrl); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return ret; @@ -750,7 +746,6 @@ static int stm32_ospi_setup(struct spi_device *spi) mutex_unlock(&ospi->lock); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; @@ -953,7 +948,6 @@ static int stm32_ospi_probe(struct platform_device *pdev) goto err_pm_resume; } - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; @@ -1032,7 +1026,6 @@ static int __maybe_unused stm32_ospi_resume(struct device *dev) writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR); writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1); - pm_runtime_mark_last_busy(ospi->dev); pm_runtime_put_autosuspend(ospi->dev); return 0; diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 9691197bbf5ad..f2d19f1c5ab1c 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -463,7 +463,6 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op * ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -487,7 +486,6 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -543,7 +541,6 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, ret = stm32_qspi_send(desc->mem->spi, &op); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret ?: len; @@ -627,7 +624,6 @@ end_of_transfer: msg->status = ret; spi_finalize_current_message(ctrl); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return ret; @@ -684,7 +680,6 @@ static int stm32_qspi_setup(struct spi_device *spi) writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); mutex_unlock(&qspi->lock); - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); return 0; @@ -858,7 +853,6 @@ static int stm32_qspi_probe(struct platform_device *pdev) if (ret) goto err_pm_runtime_free; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -938,7 +932,6 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev) writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index da3517d7102dc..d819ed4aa4fd1 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2233,7 +2233,6 @@ static int stm32_spi_probe(struct platform_device *pdev) goto err_pm_disable; } - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "driver initialized (%s mode)\n", @@ -2342,7 +2341,6 @@ static int __maybe_unused stm32_spi_resume(struct device *dev) spi->cfg->config(spi); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index a284d27945863..0b7eaccbc797e 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -158,7 +158,6 @@ static int ti_qspi_setup(struct spi_device *spi) return ret; } - pm_runtime_mark_last_busy(qspi->dev); ret = pm_runtime_put_autosuspend(qspi->dev); if (ret < 0) { dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n"); @@ -195,7 +194,6 @@ static void ti_qspi_setup_clk(struct ti_qspi *qspi, u32 speed_hz) ctx_reg->clkctrl = clk_ctrl_new; } - pm_runtime_mark_last_busy(qspi->dev); pm_runtime_put_autosuspend(qspi->dev); } diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 595b6dc108450..502fd5eccc834 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -1330,7 +1330,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1bc0fdbb1bd74..91413cc0936ae 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1723,7 +1723,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); static void spi_idle_runtime_pm(struct spi_controller *ctlr) { if (ctlr->auto_runtime_pm) { - pm_runtime_mark_last_busy(ctlr->dev.parent); pm_runtime_put_autosuspend(ctlr->dev.parent); } } @@ -3856,7 +3855,6 @@ static int spi_set_cs_timing(struct spi_device *spi) } status = spi->controller->set_cs_timing(spi); - pm_runtime_mark_last_busy(parent); pm_runtime_put_autosuspend(parent); } else { status = spi->controller->set_cs_timing(spi); @@ -3991,7 +3989,6 @@ int spi_setup(struct spi_device *spi) status = 0; spi_set_cs(spi, false, true); - pm_runtime_mark_last_busy(spi->controller->dev.parent); pm_runtime_put_autosuspend(spi->controller->dev.parent); } else { spi_set_cs(spi, false, true); -- GitLab From a094f846276068d21878b47cf552cf18ee3720fb Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Sat, 5 Jul 2025 10:53:33 +0800 Subject: [PATCH 419/868] ALSA: hda/tas2781: Add bus name in device name check Device name start from bus name, as we use strstarts() to do compare, need add it for TXNW2781 device. Fixes: b2904df0a347 ("ALSA: hda/tas2781: Add compatible for hardware id TIAS2781 and TXNW2781") Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20250705025333.24346-1-baojun.xu@ti.com Signed-off-by: Takashi Iwai --- sound/pci/hda/tas2781_hda_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index b9cdbca951e4e..530c2266ab3bb 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -588,7 +588,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; } else if (strstarts(dev_name(&clt->dev), - "TXNW2781:00-tas2781-hda.0")) { + "i2c-TXNW2781:00-tas2781-hda.0")) { device_name = "TXNW2781"; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; -- GitLab From 6eda9429501508196001845998bb8c73307d311a Mon Sep 17 00:00:00 2001 From: GalaxySnail Date: Tue, 24 Jun 2025 18:17:17 +0800 Subject: [PATCH 420/868] ALSA: hda: add MODULE_FIRMWARE for cs35l41/cs35l56 add firmware information in the .modinfo section, so that userspace tools can find out firmware required by cs35l41/cs35l56 kernel module Signed-off-by: GalaxySnail Reviewed-by: Richard Fitzgerald Link: https://patch.msgid.link/20250624101716.2365302-2-me@glxys.nl Signed-off-by: Takashi Iwai --- sound/pci/hda/cs35l41_hda.c | 2 ++ sound/pci/hda/cs35l56_hda.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index c2cf3813872a0..6155a8d2ef4e8 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -2112,3 +2112,5 @@ MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("FW_CS_DSP"); +MODULE_FIRMWARE("cirrus/cs35l41-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l41-*.bin"); diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 3f2fd32f4ad91..eedd8fdd3b8be 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -1122,3 +1122,7 @@ MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_AUTHOR("Simon Trimmer "); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("cirrus/cs35l54-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l54-*.bin"); +MODULE_FIRMWARE("cirrus/cs35l56-*.wmfw"); +MODULE_FIRMWARE("cirrus/cs35l56-*.bin"); -- GitLab From 5b32627c8ead7534c528bd57e896d0e9dcd4542a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:55:00 +0300 Subject: [PATCH 421/868] ALSA: intel_hdmi: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075500.3222950-1-sakari.ailus@linux.intel.com Signed-off-by: Takashi Iwai --- sound/x86/intel_hdmi_audio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index fe5cb41390883..cc54539c60306 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1102,7 +1102,6 @@ static int had_pcm_open(struct snd_pcm_substream *substream) return retval; error: - pm_runtime_mark_last_busy(intelhaddata->dev); pm_runtime_put_autosuspend(intelhaddata->dev); return retval; } @@ -1127,7 +1126,6 @@ static int had_pcm_close(struct snd_pcm_substream *substream) } spin_unlock_irq(&intelhaddata->had_spinlock); - pm_runtime_mark_last_busy(intelhaddata->dev); pm_runtime_put_autosuspend(intelhaddata->dev); return 0; } @@ -1589,7 +1587,6 @@ static void had_audio_wq(struct work_struct *work) } mutex_unlock(&ctx->mutex); - pm_runtime_mark_last_busy(ctx->dev); pm_runtime_put_autosuspend(ctx->dev); } -- GitLab From fc2f0135a913d0ed3aa5dab2bef193e4e23ca5a7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:55 +0300 Subject: [PATCH 422/868] ALSA: hda: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075455.3222438-1-sakari.ailus@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_device.c | 1 - sound/pci/hda/cs35l41_hda.c | 4 ---- sound/pci/hda/cs35l56_hda.c | 1 - sound/pci/hda/tas2781_hda_i2c.c | 3 --- sound/pci/hda/tas2781_hda_spi.c | 4 ---- 5 files changed, 13 deletions(-) diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 0053831eed2de..a02dce5f6a889 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -581,7 +581,6 @@ int snd_hdac_power_down(struct hdac_device *codec) { struct device *dev = &codec->dev; - pm_runtime_mark_last_busy(dev); return pm_runtime_put_autosuspend(dev); } EXPORT_SYMBOL_GPL(snd_hdac_power_down); diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index 6155a8d2ef4e8..2d7ee121a7fd4 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -836,7 +836,6 @@ static void cs35l41_hda_playback_hook(struct device *dev, int action) * Playback must be finished for all amps before we start runtime suspend. * This ensures no amps are playing back when we start putting them to sleep. */ - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: @@ -1284,7 +1283,6 @@ static void cs35l41_fw_load_work(struct work_struct *work) cs35l41->fw_request_ongoing = false; mutex_unlock(&cs35l41->fw_mutex); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_put_autosuspend(cs35l41->dev); } @@ -1515,7 +1513,6 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas dev_warn(dev, "Unable to create device link\n"); unlock_system_sleep(sleep_flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); dev_info(cs35l41->dev, @@ -2036,7 +2033,6 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); pm_runtime_use_autosuspend(cs35l41->dev); - pm_runtime_mark_last_busy(cs35l41->dev); pm_runtime_set_active(cs35l41->dev); pm_runtime_get_noresume(cs35l41->dev); pm_runtime_enable(cs35l41->dev); diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index eedd8fdd3b8be..e8b4995118bbf 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -89,7 +89,6 @@ static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56) BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) | BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT)); - pm_runtime_mark_last_busy(cs35l56->base.dev); pm_runtime_put_autosuspend(cs35l56->base.dev); } diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 530c2266ab3bb..b7ee22840d789 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -157,7 +157,6 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) tas_hda->priv->playback_started = false; mutex_unlock(&tas_hda->priv->codec_lock); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: @@ -483,7 +482,6 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) out: mutex_unlock(&tas_hda->priv->codec_lock); release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->dev); pm_runtime_put_autosuspend(tas_hda->dev); } @@ -526,7 +524,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c index 5c03e9d2283a1..c4b9a3c1a7f07 100644 --- a/sound/pci/hda/tas2781_hda_spi.c +++ b/sound/pci/hda/tas2781_hda_spi.c @@ -400,7 +400,6 @@ static void tas2781_hda_playback_hook(struct device *dev, int action) guard(mutex)(&tas_priv->codec_lock); if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK) tasdevice_tuning_switch(tas_priv, 1); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } } @@ -698,7 +697,6 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) tas2781_save_calibration(tas_hda); out: release_firmware(fmw); - pm_runtime_mark_last_busy(tas_hda->priv->dev); pm_runtime_put_autosuspend(tas_hda->priv->dev); } @@ -731,7 +729,6 @@ static int tas2781_hda_bind(struct device *dev, struct device *master, if (!ret) comp->playback_hook = tas2781_hda_playback_hook; - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -816,7 +813,6 @@ static int tas2781_hda_spi_probe(struct spi_device *spi) pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); pm_runtime_use_autosuspend(tas_priv->dev); - pm_runtime_mark_last_busy(tas_priv->dev); pm_runtime_set_active(tas_priv->dev); pm_runtime_get_noresume(tas_priv->dev); pm_runtime_enable(tas_priv->dev); -- GitLab From 9c06f26ba5f5da14bcac405c7a652dcf578a785d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 30 Apr 2025 13:56:01 +0200 Subject: [PATCH 423/868] pwm: Add support for pwmchip devices for faster and easier userspace access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change each pwmchip defining the new-style waveform callbacks can be accessed from userspace via a character device. Compared to the sysfs-API this is faster and allows to pass the whole configuration in a single ioctl allowing atomic application and thus reducing glitches. On an STM32MP13 I see: root@DistroKit:~ time pwmtestperf real 0m 1.27s user 0m 0.02s sys 0m 1.21s root@DistroKit:~ rm /dev/pwmchip0 root@DistroKit:~ time pwmtestperf real 0m 3.61s user 0m 0.27s sys 0m 3.26s pwmtestperf does essentially: for i in 0 .. 50000: pwm_set_waveform(duty_length_ns=i, period_length_ns=50000, duty_offset_ns=0) and in the presence of /dev/pwmchip0 is uses the ioctls introduced here, without that device it uses /sys/class/pwm/pwmchip0. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/ad4a4e49ae3f8ea81e23cac1ac12b338c3bf5c5b.1746010245.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 322 +++++++++++++++++++++++++++++++++++++-- include/linux/pwm.h | 3 + include/uapi/linux/pwm.h | 53 +++++++ 3 files changed, 363 insertions(+), 15 deletions(-) create mode 100644 include/uapi/linux/pwm.h diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index edf776b8ad53b..50aa0528a2653 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -23,9 +23,13 @@ #include +#include + #define CREATE_TRACE_POINTS #include +#define PWM_MINOR_COUNT 256 + /* protects access to pwm_chips */ static DEFINE_MUTEX(pwm_lock); @@ -2007,20 +2011,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } EXPORT_SYMBOL_GPL(pwm_get); -/** - * pwm_put() - release a PWM device - * @pwm: PWM device - */ -void pwm_put(struct pwm_device *pwm) +static void __pwm_put(struct pwm_device *pwm) { - struct pwm_chip *chip; - - if (!pwm) - return; - - chip = pwm->chip; - - guard(mutex)(&pwm_lock); + struct pwm_chip *chip = pwm->chip; /* * Trigger a warning if a consumer called pwm_put() twice. @@ -2041,6 +2034,20 @@ void pwm_put(struct pwm_device *pwm) module_put(chip->owner); } + +/** + * pwm_put() - release a PWM device + * @pwm: PWM device + */ +void pwm_put(struct pwm_device *pwm) +{ + if (!pwm) + return; + + guard(mutex)(&pwm_lock); + + __pwm_put(pwm); +} EXPORT_SYMBOL_GPL(pwm_put); static void devm_pwm_release(void *pwm) @@ -2110,6 +2117,274 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get); +struct pwm_cdev_data { + struct pwm_chip *chip; + struct pwm_device *pwm[]; +}; + +static int pwm_cdev_open(struct inode *inode, struct file *file) +{ + struct pwm_chip *chip = container_of(inode->i_cdev, struct pwm_chip, cdev); + struct pwm_cdev_data *cdata; + + guard(mutex)(&pwm_lock); + + if (!chip->operational) + return -ENXIO; + + cdata = kzalloc(struct_size(cdata, pwm, chip->npwm), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + cdata->chip = chip; + + file->private_data = cdata; + + return nonseekable_open(inode, file); +} + +static int pwm_cdev_release(struct inode *inode, struct file *file) +{ + struct pwm_cdev_data *cdata = file->private_data; + unsigned int i; + + for (i = 0; i < cdata->chip->npwm; ++i) { + struct pwm_device *pwm = cdata->pwm[i]; + + if (pwm) { + const char *label = pwm->label; + + pwm_put(cdata->pwm[i]); + kfree(label); + } + } + kfree(cdata); + + return 0; +} + +static int pwm_cdev_request(struct pwm_cdev_data *cdata, unsigned int hwpwm) +{ + struct pwm_chip *chip = cdata->chip; + + if (hwpwm >= chip->npwm) + return -EINVAL; + + if (!cdata->pwm[hwpwm]) { + struct pwm_device *pwm = &chip->pwms[hwpwm]; + const char *label; + int ret; + + label = kasprintf(GFP_KERNEL, "pwm-cdev (pid=%d)", current->pid); + if (!label) + return -ENOMEM; + + ret = pwm_device_request(pwm, label); + if (ret < 0) { + kfree(label); + return ret; + } + + cdata->pwm[hwpwm] = pwm; + } + + return 0; +} + +static int pwm_cdev_free(struct pwm_cdev_data *cdata, unsigned int hwpwm) +{ + struct pwm_chip *chip = cdata->chip; + + if (hwpwm >= chip->npwm) + return -EINVAL; + + if (cdata->pwm[hwpwm]) { + struct pwm_device *pwm = cdata->pwm[hwpwm]; + const char *label = pwm->label; + + __pwm_put(pwm); + + kfree(label); + + cdata->pwm[hwpwm] = NULL; + } + + return 0; +} + +static struct pwm_device *pwm_cdev_get_requested_pwm(struct pwm_cdev_data *cdata, + u32 hwpwm) +{ + struct pwm_chip *chip = cdata->chip; + + if (hwpwm >= chip->npwm) + return ERR_PTR(-EINVAL); + + if (cdata->pwm[hwpwm]) + return cdata->pwm[hwpwm]; + + return ERR_PTR(-EINVAL); +} + +static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct pwm_cdev_data *cdata = file->private_data; + struct pwm_chip *chip = cdata->chip; + + guard(mutex)(&pwm_lock); + + if (!chip->operational) + return -ENODEV; + + switch (cmd) { + case PWM_IOCTL_REQUEST: + { + unsigned int hwpwm = arg; + + return pwm_cdev_request(cdata, hwpwm); + } + + case PWM_IOCTL_FREE: + { + unsigned int hwpwm = arg; + + return pwm_cdev_free(cdata, hwpwm); + } + + case PWM_IOCTL_ROUNDWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + + if (cwf.__pad != 0) + return -EINVAL; + + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + wf = (struct pwm_waveform) { + .period_length_ns = cwf.period_length_ns, + .duty_length_ns = cwf.duty_length_ns, + .duty_offset_ns = cwf.duty_offset_ns, + }; + + ret = pwm_round_waveform_might_sleep(pwm, &wf); + if (ret < 0) + return ret; + + cwf = (struct pwmchip_waveform) { + .hwpwm = cwf.hwpwm, + .period_length_ns = wf.period_length_ns, + .duty_length_ns = wf.duty_length_ns, + .duty_offset_ns = wf.duty_offset_ns, + }; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + + case PWM_IOCTL_GETWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + + if (cwf.__pad != 0) + return -EINVAL; + + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + ret = pwm_get_waveform_might_sleep(pwm, &wf); + if (ret) + return ret; + + cwf = (struct pwmchip_waveform) { + .hwpwm = cwf.hwpwm, + .period_length_ns = wf.period_length_ns, + .duty_length_ns = wf.duty_length_ns, + .duty_offset_ns = wf.duty_offset_ns, + }; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + + case PWM_IOCTL_SETROUNDEDWF: + case PWM_IOCTL_SETEXACTWF: + { + struct pwmchip_waveform cwf; + struct pwm_waveform wf; + struct pwm_device *pwm; + + ret = copy_from_user(&cwf, + (struct pwmchip_waveform __user *)arg, + sizeof(cwf)); + if (ret) + return -EFAULT; + + if (cwf.__pad != 0) + return -EINVAL; + + wf = (struct pwm_waveform){ + .period_length_ns = cwf.period_length_ns, + .duty_length_ns = cwf.duty_length_ns, + .duty_offset_ns = cwf.duty_offset_ns, + }; + + if (!pwm_wf_valid(&wf)) + return -EINVAL; + + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + ret = pwm_set_waveform_might_sleep(pwm, &wf, + cmd == PWM_IOCTL_SETEXACTWF); + + /* + * If userspace cares about rounding deviations it has + * to check the values anyhow, so simplify handling for + * them and don't signal uprounding. This matches the + * behaviour of PWM_IOCTL_ROUNDWF which also returns 0 + * in that case. + */ + if (ret == 1) + ret = 0; + + return ret; + } + + default: + return -ENOTTY; + } +} + +static const struct file_operations pwm_cdev_fileops = { + .open = pwm_cdev_open, + .release = pwm_cdev_release, + .owner = THIS_MODULE, + .unlocked_ioctl = pwm_cdev_ioctl, +}; + +static dev_t pwm_devt; + /** * __pwmchip_add() - register a new PWM chip * @chip: the PWM chip to add @@ -2162,7 +2437,17 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner) scoped_guard(pwmchip, chip) chip->operational = true; - ret = device_add(&chip->dev); + if (chip->ops->write_waveform) { + if (chip->id < PWM_MINOR_COUNT) + chip->dev.devt = MKDEV(MAJOR(pwm_devt), chip->id); + else + dev_warn(&chip->dev, "chip id too high to create a chardev\n"); + } + + cdev_init(&chip->cdev, &pwm_cdev_fileops); + chip->cdev.owner = owner; + + ret = cdev_device_add(&chip->cdev, &chip->dev); if (ret) goto err_device_add; @@ -2213,7 +2498,7 @@ void pwmchip_remove(struct pwm_chip *chip) idr_remove(&pwm_chips, chip->id); } - device_del(&chip->dev); + cdev_device_del(&chip->cdev, &chip->dev); } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -2357,9 +2642,16 @@ static int __init pwm_init(void) { int ret; + ret = alloc_chrdev_region(&pwm_devt, 0, PWM_MINOR_COUNT, "pwm"); + if (ret) { + pr_err("Failed to initialize chrdev region for PWM usage\n"); + return ret; + } + ret = class_register(&pwm_class); if (ret) { pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret)); + unregister_chrdev_region(pwm_devt, 256); return ret; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 63a17d2b4ec8d..2492c91452f96 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -2,6 +2,7 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H +#include #include #include #include @@ -311,6 +312,7 @@ struct pwm_ops { /** * struct pwm_chip - abstract a PWM controller * @dev: device providing the PWMs + * @cdev: &struct cdev for this device * @ops: callbacks for this PWM controller * @owner: module providing this chip * @id: unique number of this PWM chip @@ -325,6 +327,7 @@ struct pwm_ops { */ struct pwm_chip { struct device dev; + struct cdev cdev; const struct pwm_ops *ops; struct module *owner; unsigned int id; diff --git a/include/uapi/linux/pwm.h b/include/uapi/linux/pwm.h new file mode 100644 index 0000000000000..182d59cc07eec --- /dev/null +++ b/include/uapi/linux/pwm.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ + +#ifndef _UAPI_PWM_H_ +#define _UAPI_PWM_H_ + +#include +#include + +/** + * struct pwmchip_waveform - Describe a PWM waveform for a pwm_chip's PWM channel + * @hwpwm: per-chip relative index of the PWM device + * @__pad: padding, must be zero + * @period_length_ns: duration of the repeating period. + * A value of 0 represents a disabled PWM. + * @duty_length_ns: duration of the active part in each period + * @duty_offset_ns: offset of the rising edge from a period's start + */ +struct pwmchip_waveform { + __u32 hwpwm; + __u32 __pad; + __u64 period_length_ns; + __u64 duty_length_ns; + __u64 duty_offset_ns; +}; + +/* Reserves the passed hwpwm for exclusive control. */ +#define PWM_IOCTL_REQUEST _IO(0x75, 1) + +/* counter part to PWM_IOCTL_REQUEST */ +#define PWM_IOCTL_FREE _IO(0x75, 2) + +/* + * Modifies the passed wf according to hardware constraints. All parameters are + * rounded down to the next possible value, unless there is no such value, then + * values are rounded up. Note that zero isn't considered for rounding down + * period_length_ns. + */ +#define PWM_IOCTL_ROUNDWF _IOWR(0x75, 3, struct pwmchip_waveform) + +/* Get the currently implemented waveform */ +#define PWM_IOCTL_GETWF _IOWR(0x75, 4, struct pwmchip_waveform) + +/* Like PWM_IOCTL_ROUNDWF + PWM_IOCTL_SETEXACTWF in one go. */ +#define PWM_IOCTL_SETROUNDEDWF _IOW(0x75, 5, struct pwmchip_waveform) + +/* + * Program the PWM to emit exactly the passed waveform, subject only to rounding + * down each value less than 1 ns. Returns 0 on success, -EDOM if the waveform + * cannot be implemented exactly, or other negative error codes. + */ +#define PWM_IOCTL_SETEXACTWF _IOW(0x75, 6, struct pwmchip_waveform) + +#endif /* _UAPI_PWM_H_ */ -- GitLab From 08e0b981231fd467204764f162087d6d9d1359af Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 29 Apr 2025 16:50:43 +0800 Subject: [PATCH 424/868] dt-bindings: pwm: marvell,pxa-pwm: Add SpacemiT K1 PWM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SpacemiT K1 SoC reuses the Marvell PXA910-compatible PWM controller with one notable difference: the addition of a resets property. To make the device tree pass schema validation (make dtbs_check W=3), this patch updates the binding to accept spacemit,k1-pwm as a compatible string, when used in conjunction with the fallback marvell,pxa910-pwm. Support for the optional resets property is also added, as it is required by the K1 integration but was not present in the original Marvell bindings. Since the PWM reset line may be deasserted during the early bootloader stage, making the resets property optional avoids potential double-deassertion, which could otherwise cause flickering on displays that use PWM for backlight control. Additionally, this patch adjusts the required value of the #pwm-cells property for the new compatible string: - For "spacemit,k1-pwm", #pwm-cells must be set to 3. - For existing Marvell compatibles, #pwm-cells remains 1. Background of #pwm-cells change is by an ongoing community discussion about increasing the #pwm-cells value from 1 to 3 for all Marvell PXA PWM devices. These devices are currently the only ones whose bindings do not pass the line index as the first argument. See [1] for further details. [1] https://lore.kernel.org/all/cover.1738842938.git.u.kleine-koenig@baylibre.com/ Reviewed-by: Rob Herring (Arm) # v2 Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250429085048.1310409-2-guodong@riscstar.com Signed-off-by: Uwe Kleine-König --- .../bindings/pwm/marvell,pxa-pwm.yaml | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml index 9ee1946dc2e12..8df327e528107 100644 --- a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml @@ -11,26 +11,47 @@ maintainers: allOf: - $ref: pwm.yaml# + - if: + properties: + compatible: + contains: + const: spacemit,k1-pwm + then: + properties: + "#pwm-cells": + const: 3 + else: + properties: + "#pwm-cells": + const: 1 + description: | + Used for specifying the period length in nanoseconds. properties: compatible: - enum: - - marvell,pxa250-pwm - - marvell,pxa270-pwm - - marvell,pxa168-pwm - - marvell,pxa910-pwm + oneOf: + - enum: + - marvell,pxa250-pwm + - marvell,pxa270-pwm + - marvell,pxa168-pwm + - marvell,pxa910-pwm + - items: + - const: spacemit,k1-pwm + - const: marvell,pxa910-pwm reg: # Length should be 0x10 maxItems: 1 "#pwm-cells": - # Used for specifying the period length in nanoseconds - const: 1 + description: Number of cells in a pwm specifier. clocks: maxItems: 1 + resets: + maxItems: 1 + required: - compatible - reg -- GitLab From 52d2d14d9e49b8c33ff718d2036084d0c92f23f1 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 29 Apr 2025 16:50:44 +0800 Subject: [PATCH 425/868] pwm: pxa: Add optional reset control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support optional reset control for the PWM PXA driver. During probe, it acquires the reset controller using devm_reset_control_get_optional_exclusive_deasserted() to get and deassert the reset controller to enable the PWM channel. Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250429085048.1310409-3-guodong@riscstar.com [ukleinek: Fix conflict with commit df08fff8add2 ("pwm: pxa: Improve using dev_err_probe()")] Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-pxa.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 8a4a3d2df30de..0f5bdb0e395ea 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -161,6 +162,7 @@ static int pwm_probe(struct platform_device *pdev) struct pwm_chip *chip; struct pxa_pwm_chip *pc; struct device *dev = &pdev->dev; + struct reset_control *rst; int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) @@ -179,6 +181,10 @@ static int pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->clk)) return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + chip->ops = &pxa_pwm_ops; if (IS_ENABLED(CONFIG_OF)) -- GitLab From 27b5dfe4b4eaf490df6fa9d5d0bf95cd51abf563 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 29 Apr 2025 16:50:47 +0800 Subject: [PATCH 426/868] pwm: pxa: Allow to enable for SpacemiT K1 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SpacemiT K1 SoC uses devices similar to the ones on PXA SoCs. Add ARCH_SPACEMIT as one of the possible architectures this driver can be enabled for. Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250429085048.1310409-6-guodong@riscstar.com [ukleinek: reword commit log] Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index d9bcd1e8413ea..6e113f8b4baf2 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -517,7 +517,7 @@ config PWM_PCA9685 config PWM_PXA tristate "PXA PWM support" - depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST + depends on ARCH_PXA || ARCH_MMP || ARCH_SPACEMIT || COMPILE_TEST depends on HAS_IOMEM help Generic PWM framework driver for PXA. -- GitLab From f4bcf818e5d6474c981ef16153827458a2b57181 Mon Sep 17 00:00:00 2001 From: Nylon Chen Date: Thu, 29 May 2025 11:53:39 +0800 Subject: [PATCH 427/868] riscv: dts: sifive: unleashed/unmatched: Remove PWM controlled LED's active-low properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the active-low properties of the PWM-controlled LEDs in the HiFive Unmatched device tree. The reference is hifive-unleashed-a00.pdf[0] and hifive-unmatched-schematics-v3.pdf[1]. Link: https://sifive.cdn.prismic.io/sifive/c52a8e32-05ce-4aaf-95c8-7bf8453f8698_hifive-unleashed-a00-schematics-1.pdf [0] Link: https://sifive.cdn.prismic.io/sifive/6a06d6c0-6e66-49b5-8e9e-e68ce76f4192_hifive-unmatched-schematics-v3.pdf [1] Acked-by: Conor Dooley Reviewed-by: Conor Dooley Signed-off-by: Vincent Chen Signed-off-by: Nylon Chen Link: https://lore.kernel.org/r/20250529035341.51736-2-nylon.chen@sifive.com Signed-off-by: Uwe Kleine-König --- arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts | 12 ++++-------- arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 12 ++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts index 900a50526d771..06731b8c7bc3b 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts @@ -49,32 +49,28 @@ compatible = "pwm-leds"; led-d1 { - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 0 7812500 0>; color = ; max-brightness = <255>; label = "d1"; }; led-d2 { - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 1 7812500 0>; color = ; max-brightness = <255>; label = "d2"; }; led-d3 { - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 2 7812500 0>; color = ; max-brightness = <255>; label = "d3"; }; led-d4 { - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 3 7812500 0>; color = ; max-brightness = <255>; label = "d4"; diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts index 72b87b08ab444..03ce2cee4e976 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts @@ -51,8 +51,7 @@ compatible = "pwm-leds"; led-d12 { - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 0 7812500 0>; color = ; max-brightness = <255>; label = "d12"; @@ -68,20 +67,17 @@ label = "d2"; led-red { - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 2 7812500 0>; color = ; }; led-green { - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 1 7812500 0>; color = ; }; led-blue { - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; - active-low; + pwms = <&pwm0 3 7812500 0>; color = ; }; }; -- GitLab From 7dbc4432ea6bf9d709391eb57f1e9fb44e99845a Mon Sep 17 00:00:00 2001 From: Nylon Chen Date: Thu, 29 May 2025 11:53:40 +0800 Subject: [PATCH 428/868] pwm: sifive: Fix PWM algorithm and clarify inverted compare behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `frac` variable represents the pulse inactive time, and the result of this algorithm is the pulse active time. Therefore, we must reverse the result. Although the SiFive Reference Manual states "pwms >= pwmcmpX -> HIGH", the hardware behavior is inverted due to a fixed XNOR with 0. As a result, the pwmcmp register actually defines the low (inactive) portion of the pulse. The reference is SiFive FU740-C000 Manual[0] Link: https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf [0] Co-developed-by: Zong Li Signed-off-by: Zong Li Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Nylon Chen Link: https://lore.kernel.org/r/20250529035341.51736-3-nylon.chen@sifive.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sifive.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index d5b647e6be781..f3694801d3eed 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -4,11 +4,28 @@ * For SiFive's PWM IP block documentation please refer Chapter 14 of * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf * + * PWM output inversion: According to the SiFive Reference manual + * the output of each comparator is high whenever the value of pwms is + * greater than or equal to the corresponding pwmcmpX[Reference Manual]. + * + * Figure 29 in the same manual shows that the pwmcmpXcenter bit is + * hard-tied to 0 (XNOR), which effectively inverts the comparison so that + * the output goes HIGH when `pwms < pwmcmpX`. + * + * In other words, each pwmcmp register actually defines the **inactive** + * (low) period of the pulse, not the active time exactly opposite to what + * the documentation text implies. + * + * To compensate, this driver always **inverts** the duty value when reading + * or writing pwmcmp registers , so that users interact with a conventional + * **active-high** PWM interface. + * + * * Limitations: * - When changing both duty cycle and period, we cannot prevent in * software that the output might produce a period with mixed * settings (new period length and old duty cycle). - * - The hardware cannot generate a 100% duty cycle. + * - The hardware cannot generate a 0% duty cycle. * - The hardware generates only inverted output. */ #include @@ -110,9 +127,14 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip); - u32 duty, val; + u32 duty, val, inactive; - duty = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); + inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); + /* + * PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty. + * Here, 'inactive' is the low time and we compute duty as max_count - inactive. + */ + duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive; state->enabled = duty > 0; @@ -123,7 +145,7 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->period = ddata->real_period; state->duty_cycle = (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; - state->polarity = PWM_POLARITY_INVERSED; + state->polarity = PWM_POLARITY_NORMAL; return 0; } @@ -137,9 +159,9 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long num; bool enabled; int ret = 0; - u32 frac; + u32 frac, inactive; - if (state->polarity != PWM_POLARITY_INVERSED) + if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; cur_state = pwm->state; @@ -157,8 +179,9 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, */ num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH); frac = DIV64_U64_ROUND_CLOSEST(num, state->period); - /* The hardware cannot generate a 100% duty cycle */ + /* The hardware cannot generate a 0% duty cycle */ frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); + inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac; mutex_lock(&ddata->lock); if (state->period != ddata->approx_period) { @@ -190,7 +213,7 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, } } - writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); + writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); if (!state->enabled) clk_disable(ddata->clk); -- GitLab From 6df3aac763fa995260ab0545c1e54f0c21b2feb8 Mon Sep 17 00:00:00 2001 From: Nylon Chen Date: Thu, 29 May 2025 11:53:41 +0800 Subject: [PATCH 429/868] pwm: sifive: Fix rounding and idempotency issues in apply and get_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fix ensures consistent rounding and avoids mismatches between applied and reported PWM values that could trigger false idempotency failures in debug checks This change ensures: - real_period is now calculated using DIV_ROUND_UP_ULL() to avoid underestimation. - duty_cycle is rounded up to match the fractional computation in apply() - apply() truncates the result to compensate for get_state's rounding up logic These fixes resolve issues like: .apply is supposed to round down duty_cycle (requested: 360/504000, applied: 361/504124) .apply is not idempotent (ena=1 pol=0 1739692/4032985) -> (ena=1 pol=0 1739630/4032985) Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202505080303.dBfU5YMS-lkp@intel.com/ Co-developed-by: Zong Li Signed-off-by: Zong Li Signed-off-by: Nylon Chen Link: https://lore.kernel.org/r/20250529035341.51736-4-nylon.chen@sifive.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sifive.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index f3694801d3eed..4a07315b07442 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -118,7 +118,7 @@ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata, /* As scale <= 15 the shift operation cannot overflow. */ num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale); - ddata->real_period = div64_ul(num, rate); + ddata->real_period = DIV_ROUND_UP_ULL(num, rate); dev_dbg(ddata->parent, "New real_period = %u ns\n", ddata->real_period); } @@ -143,8 +143,8 @@ static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->enabled = false; state->period = ddata->real_period; - state->duty_cycle = - (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; + state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, + (1U << PWM_SIFIVE_CMPWIDTH)); state->polarity = PWM_POLARITY_NORMAL; return 0; @@ -159,7 +159,8 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long num; bool enabled; int ret = 0; - u32 frac, inactive; + u64 frac; + u32 inactive; if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; @@ -178,9 +179,11 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, * consecutively */ num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH); - frac = DIV64_U64_ROUND_CLOSEST(num, state->period); + frac = num; + do_div(frac, state->period); /* The hardware cannot generate a 0% duty cycle */ - frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); + frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1); + /* pwmcmp register must be loaded with the inactive(invert the duty) */ inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac; mutex_lock(&ddata->lock); -- GitLab From 2b66b67530b828ce939c050d7d3366a80ef22d7a Mon Sep 17 00:00:00 2001 From: Longbin Li Date: Wed, 28 May 2025 18:11:36 +0800 Subject: [PATCH 430/868] dt-bindings: pwm: sophgo: Add pwm controller for SG2044 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add compatible string for PWM controller on SG2044. Tested-by: Chen Wang Reviewed-by: Chen Wang Acked-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Signed-off-by: Longbin Li Link: https://lore.kernel.org/r/20250528101139.28702-2-looong.bin@gmail.com Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml b/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml index bbb6326d47d76..e0e91aa237ec5 100644 --- a/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml @@ -17,7 +17,9 @@ allOf: properties: compatible: - const: sophgo,sg2042-pwm + enum: + - sophgo,sg2042-pwm + - sophgo,sg2044-pwm reg: maxItems: 1 -- GitLab From 8c805dfafd9b9d82a4bdb2e804d85f3be0435aeb Mon Sep 17 00:00:00 2001 From: Longbin Li Date: Wed, 28 May 2025 18:11:37 +0800 Subject: [PATCH 431/868] pwm: sophgo-sg2042: Reorganize the code structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the driver logic can be used in both SG2042 and SG2044, it will be better to reorganize the code structure. Reviewed-by: Chen Wang Tested-by: Chen Wang Signed-off-by: Longbin Li Link: https://lore.kernel.org/r/20250528101139.28702-3-looong.bin@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sophgo-sg2042.c | 52 +++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c index ff4639d849ce1..f3956563037fa 100644 --- a/drivers/pwm/pwm-sophgo-sg2042.c +++ b/drivers/pwm/pwm-sophgo-sg2042.c @@ -53,6 +53,10 @@ struct sg2042_pwm_ddata { unsigned long clk_rate_hz; }; +struct sg2042_chip_data { + const struct pwm_ops ops; +}; + /* * period_ticks: PERIOD * hlperiod_ticks: HLPERIOD @@ -66,21 +70,13 @@ static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan, writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan)); } -static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) +static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) { struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); u32 hlperiod_ticks; u32 period_ticks; - if (state->polarity == PWM_POLARITY_INVERSED) - return -EINVAL; - - if (!state->enabled) { - pwm_sg2042_config(ddata, pwm->hwpwm, 0, 0); - return 0; - } - /* * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk * Duration of One Cycle (period) = PERIOD x Period_of_input_clk @@ -92,6 +88,22 @@ static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, pwm->hwpwm, period_ticks, hlperiod_ticks); pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks); +} + +static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); + + if (state->polarity == PWM_POLARITY_INVERSED) + return -EINVAL; + + if (!state->enabled) { + pwm_sg2042_config(ddata, pwm->hwpwm, 0, 0); + return 0; + } + + pwm_sg2042_set_dutycycle(chip, pwm, state); return 0; } @@ -123,13 +135,18 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } -static const struct pwm_ops pwm_sg2042_ops = { - .apply = pwm_sg2042_apply, - .get_state = pwm_sg2042_get_state, +static const struct sg2042_chip_data sg2042_chip_data = { + .ops = { + .apply = pwm_sg2042_apply, + .get_state = pwm_sg2042_get_state, + }, }; static const struct of_device_id sg2042_pwm_ids[] = { - { .compatible = "sophgo,sg2042-pwm" }, + { + .compatible = "sophgo,sg2042-pwm", + .data = &sg2042_chip_data + }, { } }; MODULE_DEVICE_TABLE(of, sg2042_pwm_ids); @@ -137,12 +154,17 @@ MODULE_DEVICE_TABLE(of, sg2042_pwm_ids); static int pwm_sg2042_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct sg2042_chip_data *chip_data; struct sg2042_pwm_ddata *ddata; struct reset_control *rst; struct pwm_chip *chip; struct clk *clk; int ret; + chip_data = device_get_match_data(dev); + if (!chip_data) + return -ENODEV; + chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata)); if (IS_ERR(chip)) return PTR_ERR(chip); @@ -170,7 +192,7 @@ static int pwm_sg2042_probe(struct platform_device *pdev) if (IS_ERR(rst)) return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n"); - chip->ops = &pwm_sg2042_ops; + chip->ops = &chip_data->ops; chip->atomic = true; ret = devm_pwmchip_add(dev, chip); -- GitLab From 21d5daad93547c36732b6568dfb8ad88004b2d68 Mon Sep 17 00:00:00 2001 From: Longbin Li Date: Wed, 28 May 2025 18:11:38 +0800 Subject: [PATCH 432/868] pwm: sophgo-sg2042: Add support for SG2044 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add PWM controller for SG2044 on base of SG2042. Reviewed-by: Chen Wang Tested-by: Chen Wang Signed-off-by: Longbin Li Link: https://lore.kernel.org/r/20250528101139.28702-4-looong.bin@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sophgo-sg2042.c | 89 ++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c index f3956563037fa..7d07b0ca7d298 100644 --- a/drivers/pwm/pwm-sophgo-sg2042.c +++ b/drivers/pwm/pwm-sophgo-sg2042.c @@ -13,6 +13,7 @@ * the running period. * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will * be stopped and the output is pulled to high. + * - SG2044 supports both polarities, SG2042 only normal polarity. * See the datasheet [1] for more details. * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM */ @@ -41,6 +42,10 @@ #define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0) #define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4) +#define SG2044_PWM_POLARITY 0x40 +#define SG2044_PWM_PWMSTART 0x44 +#define SG2044_PWM_OE 0xd0 + #define SG2042_PWM_CHANNELNUM 4 /** @@ -84,8 +89,8 @@ static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *p period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX); hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX); - dev_dbg(pwmchip_parent(chip), "chan[%u]: PERIOD=%u, HLPERIOD=%u\n", - pwm->hwpwm, period_ticks, hlperiod_ticks); + dev_dbg(pwmchip_parent(chip), "chan[%u]: ENABLE=%u, PERIOD=%u, HLPERIOD=%u, POLARITY=%u\n", + pwm->hwpwm, state->enabled, period_ticks, hlperiod_ticks, state->polarity); pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks); } @@ -135,6 +140,74 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static void pwm_sg2044_set_outputen(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, + bool enabled) +{ + u32 pwmstart; + + pwmstart = readl(ddata->base + SG2044_PWM_PWMSTART); + + if (enabled) + pwmstart |= BIT(pwm->hwpwm); + else + pwmstart &= ~BIT(pwm->hwpwm); + + writel(pwmstart, ddata->base + SG2044_PWM_PWMSTART); +} + +static void pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, + bool enabled) +{ + u32 pwm_oe; + + pwm_oe = readl(ddata->base + SG2044_PWM_OE); + + if (enabled) + pwm_oe |= BIT(pwm->hwpwm); + else + pwm_oe &= ~BIT(pwm->hwpwm); + + writel(pwm_oe, ddata->base + SG2044_PWM_OE); +} + +static void pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, + const struct pwm_state *state) +{ + u32 pwm_polarity; + + pwm_polarity = readl(ddata->base + SG2044_PWM_POLARITY); + + if (state->polarity == PWM_POLARITY_NORMAL) + pwm_polarity &= ~BIT(pwm->hwpwm); + else + pwm_polarity |= BIT(pwm->hwpwm); + + writel(pwm_polarity, ddata->base + SG2044_PWM_POLARITY); +} + +static int pwm_sg2044_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); + + pwm_sg2044_set_polarity(ddata, pwm, state); + + pwm_sg2042_set_dutycycle(chip, pwm, state); + + /* + * re-enable PWMSTART to refresh the register period + */ + pwm_sg2044_set_outputen(ddata, pwm, false); + + if (!state->enabled) + return 0; + + pwm_sg2044_set_outputdir(ddata, pwm, true); + pwm_sg2044_set_outputen(ddata, pwm, true); + + return 0; +} + static const struct sg2042_chip_data sg2042_chip_data = { .ops = { .apply = pwm_sg2042_apply, @@ -142,11 +215,22 @@ static const struct sg2042_chip_data sg2042_chip_data = { }, }; +static const struct sg2042_chip_data sg2044_chip_data = { + .ops = { + .apply = pwm_sg2044_apply, + .get_state = pwm_sg2042_get_state, + }, +}; + static const struct of_device_id sg2042_pwm_ids[] = { { .compatible = "sophgo,sg2042-pwm", .data = &sg2042_chip_data }, + { + .compatible = "sophgo,sg2044-pwm", + .data = &sg2044_chip_data + }, { } }; MODULE_DEVICE_TABLE(of, sg2042_pwm_ids); @@ -212,5 +296,6 @@ static struct platform_driver pwm_sg2042_driver = { module_platform_driver(pwm_sg2042_driver); MODULE_AUTHOR("Chen Wang"); +MODULE_AUTHOR("Longbin Li "); MODULE_DESCRIPTION("Sophgo SG2042 PWM driver"); MODULE_LICENSE("GPL"); -- GitLab From 076a2f3d54a95330709a39c95bde7f19660673da Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 29 May 2025 11:53:18 -0500 Subject: [PATCH 433/868] dt-bindings: pwm: adi,axi-pwmgen: Update documentation link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the documentation link to point to the location with the most up-to-date information. Signed-off-by: David Lechner Link: https://lore.kernel.org/r/20250529-pwm-axi-pwmgen-add-external-clock-v3-1-5d8809a7da91@baylibre.com Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml index 5575c58357d6e..e4c2d5186dedb 100644 --- a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml +++ b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml @@ -14,7 +14,7 @@ description: The Analog Devices AXI PWM generator can generate PWM signals with variable pulse width and period. - https://wiki.analog.com/resources/fpga/docs/axi_pwm_gen + https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html allOf: - $ref: pwm.yaml# -- GitLab From fd0b06972a8f92d57358e62267f5925721c73c6e Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Fri, 10 Jan 2025 10:19:18 +0100 Subject: [PATCH 434/868] pwm: stm32: add support for stm32mp25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for STM32MP25 SoC. Use newly introduced compatible to handle new features along with registers and bits diversity. The MFD part of the driver fills in ipidr, so it is used to check the hardware configuration register, when available to gather the number of PWM channels and complementary outputs. Signed-off-by: Fabrice Gasnier Link: https://lore.kernel.org/r/20250110091922.980627-5-fabrice.gasnier@foss.st.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-stm32.c | 42 ++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 4b148f0afeb99..2594fb771b04a 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -19,6 +19,7 @@ #define CCMR_CHANNEL_SHIFT 8 #define CCMR_CHANNEL_MASK 0xFF #define MAX_BREAKINPUT 2 +#define STM32_MAX_PWM_OUTPUT 4 struct stm32_breakinput { u32 index; @@ -768,10 +769,19 @@ static int stm32_pwm_probe_breakinputs(struct stm32_pwm *priv, return stm32_pwm_apply_breakinputs(priv); } -static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) +static void stm32_pwm_detect_complementary(struct stm32_pwm *priv, struct stm32_timers *ddata) { u32 ccer; + if (ddata->ipidr) { + u32 val; + + /* Simply read from HWCFGR the number of complementary outputs (MP25). */ + regmap_read(priv->regmap, TIM_HWCFGR1, &val); + priv->have_complementary_output = !!FIELD_GET(TIM_HWCFGR1_NB_OF_DT, val); + return; + } + /* * If complementary bit doesn't exist writing 1 will have no * effect so we can detect it. @@ -783,22 +793,39 @@ static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) priv->have_complementary_output = (ccer != 0); } -static unsigned int stm32_pwm_detect_channels(struct regmap *regmap, +static unsigned int stm32_pwm_detect_channels(struct stm32_timers *ddata, unsigned int *num_enabled) { + struct regmap *regmap = ddata->regmap; u32 ccer, ccer_backup; + regmap_read(regmap, TIM_CCER, &ccer_backup); + *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); + + if (ddata->ipidr) { + u32 hwcfgr; + unsigned int npwm; + + /* Deduce from HWCFGR the number of outputs (MP25). */ + regmap_read(regmap, TIM_HWCFGR1, &hwcfgr); + + /* + * Timers may have more capture/compare channels than the + * actual number of PWM channel outputs (e.g. TIM_CH[1..4]). + */ + npwm = FIELD_GET(TIM_HWCFGR1_NB_OF_CC, hwcfgr); + + return npwm < STM32_MAX_PWM_OUTPUT ? npwm : STM32_MAX_PWM_OUTPUT; + } + /* * If channels enable bits don't exist writing 1 will have no * effect so we can detect and count them. */ - regmap_read(regmap, TIM_CCER, &ccer_backup); regmap_set_bits(regmap, TIM_CCER, TIM_CCER_CCXE); regmap_read(regmap, TIM_CCER, &ccer); regmap_write(regmap, TIM_CCER, ccer_backup); - *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); - return hweight32(ccer & TIM_CCER_CCXE); } @@ -813,7 +840,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) unsigned int i; int ret; - npwm = stm32_pwm_detect_channels(ddata->regmap, &num_enabled); + npwm = stm32_pwm_detect_channels(ddata, &num_enabled); chip = devm_pwmchip_alloc(dev, npwm, sizeof(*priv)); if (IS_ERR(chip)) @@ -834,7 +861,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to configure breakinputs\n"); - stm32_pwm_detect_complementary(priv); + stm32_pwm_detect_complementary(priv, ddata); ret = devm_clk_rate_exclusive_get(dev, priv->clk); if (ret) @@ -907,6 +934,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_r static const struct of_device_id stm32_pwm_of_match[] = { { .compatible = "st,stm32-pwm", }, + { .compatible = "st,stm32mp25-pwm", }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, stm32_pwm_of_match); -- GitLab From 0b4d1abe5ca568c5b7f667345ec2b5ad0fb2e54b Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Mon, 16 Jun 2025 17:14:17 +0200 Subject: [PATCH 435/868] pwm: rockchip: Round period/duty down on apply, up on get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_PWM_DEBUG=y, the rockchip PWM driver produces warnings like this: rockchip-pwm fd8b0010.pwm: .apply is supposed to round down duty_cycle (requested: 23529/50000, applied: 23542/50000) This is because the driver chooses ROUND_CLOSEST for purported idempotency reasons. However, it's possible to keep idempotency while always rounding down in .apply(). Do this by making .get_state() always round up, and making .apply() always round down. This is done with u64 maths, and setting both period and duty to U32_MAX (the biggest the hardware can support) if they would exceed their 32 bits confines. Fixes: 12f9ce4a5198 ("pwm: rockchip: Fix period and duty cycle approximation") Fixes: 1ebb74cf3537 ("pwm: rockchip: Add support for hardware readout") Signed-off-by: Nicolas Frattaroli Link: https://lore.kernel.org/r/20250616-rockchip-pwm-rounding-fix-v2-1-a9c65acad7b6@collabora.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-rockchip.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index c5f50e5eaf41a..67b85bdb491b1 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -61,6 +63,7 @@ static int rockchip_pwm_get_state(struct pwm_chip *chip, struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; u32 enable_conf = pc->data->enable_conf; unsigned long clk_rate; u64 tmp; @@ -78,12 +81,12 @@ static int rockchip_pwm_get_state(struct pwm_chip *chip, clk_rate = clk_get_rate(pc->clk); tmp = readl_relaxed(pc->base + pc->data->regs.period); - tmp *= pc->data->prescaler * NSEC_PER_SEC; - state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + tmp *= prescaled_ns; + state->period = DIV_U64_ROUND_UP(tmp, clk_rate); tmp = readl_relaxed(pc->base + pc->data->regs.duty); - tmp *= pc->data->prescaler * NSEC_PER_SEC; - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + tmp *= prescaled_ns; + state->duty_cycle = DIV_U64_ROUND_UP(tmp, clk_rate); val = readl_relaxed(pc->base + pc->data->regs.ctrl); state->enabled = (val & enable_conf) == enable_conf; @@ -103,8 +106,9 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - unsigned long period, duty; - u64 clk_rate, div; + u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; + u64 clk_rate, tmp; + u32 period_ticks, duty_ticks; u32 ctrl; clk_rate = clk_get_rate(pc->clk); @@ -114,12 +118,15 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * bits, every possible input period can be obtained using the * default prescaler value for all practical clock rate values. */ - div = clk_rate * state->period; - period = DIV_ROUND_CLOSEST_ULL(div, - pc->data->prescaler * NSEC_PER_SEC); + tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns); + if (tmp > U32_MAX) + tmp = U32_MAX; + period_ticks = tmp; - div = clk_rate * state->duty_cycle; - duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + tmp = mul_u64_u64_div_u64(clk_rate, state->duty_cycle, prescaled_ns); + if (tmp > U32_MAX) + tmp = U32_MAX; + duty_ticks = tmp; /* * Lock the period and duty of previous configuration, then @@ -131,8 +138,8 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); } - writel(period, pc->base + pc->data->regs.period); - writel(duty, pc->base + pc->data->regs.duty); + writel(period_ticks, pc->base + pc->data->regs.period); + writel(duty_ticks, pc->base + pc->data->regs.duty); if (pc->data->supports_polarity) { ctrl &= ~PWM_POLARITY_MASK; -- GitLab From 56ad79b848d4a1e9ae270687f6f41835911eba87 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 16 Jun 2025 15:04:34 -0400 Subject: [PATCH 436/868] dt-bindings: pwm: convert lpc1850-sct-pwm.txt to yaml format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert lpc1850-sct-pwm.txt to yaml format. Additional changes: - add ref pwm.yaml. - add resets property to match existed dts. Reviewed-by: Rob Herring (Arm) Reviewed-by: Vladimir Zapolskiy Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20250616190435.1998078-1-Frank.Li@nxp.com Signed-off-by: Uwe Kleine-König --- .../bindings/pwm/lpc1850-sct-pwm.txt | 20 ------- .../bindings/pwm/nxp,lpc1850-sct-pwm.yaml | 54 +++++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt create mode 100644 Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt deleted file mode 100644 index 43d9f4f08a2e2..0000000000000 --- a/Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt +++ /dev/null @@ -1,20 +0,0 @@ -* NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver - -Required properties: - - compatible: Should be "nxp,lpc1850-sct-pwm" - - reg: Should contain physical base address and length of pwm registers. - - clocks: Must contain an entry for each entry in clock-names. - See ../clock/clock-bindings.txt for details. - - clock-names: Must include the following entries. - - pwm: PWM operating clock. - - #pwm-cells: Should be 3. See pwm.yaml in this directory for the description - of the cells format. - -Example: - pwm: pwm@40000000 { - compatible = "nxp,lpc1850-sct-pwm"; - reg = <0x40000000 0x1000>; - clocks =<&ccu1 CLK_CPU_SCT>; - clock-names = "pwm"; - #pwm-cells = <3>; - }; diff --git a/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml b/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml new file mode 100644 index 0000000000000..ffda0123878ed --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/nxp,lpc1850-sct-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx State Configurable Timer + +maintainers: + - Frank Li + +properties: + compatible: + const: nxp,lpc1850-sct-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: pwm + + '#pwm-cells': + const: 3 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#pwm-cells' + +allOf: + - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + pwm@40000000 { + compatible = "nxp,lpc1850-sct-pwm"; + reg = <0x40000000 0x1000>; + clocks =<&ccu1 CLK_CPU_SCT>; + clock-names = "pwm"; + #pwm-cells = <3>; + }; -- GitLab From 3bb9948921784b6c9cc2f5ed8df7bdf8ab4ffaf3 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:16 +0200 Subject: [PATCH 437/868] dt-bindings: pwm: mediatek,mt2712-pwm: Add support for MT6991/MT8196 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add compatible strings for the MediaTek Dimensity 9400 MT6991 and for the MT8196 Chromebook SoC, having the same PWM IP v3.0.2. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20250623120118.109170-2-angelogioacchino.delregno@collabora.com Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml index d515c09e10217..fc31758a40b06 100644 --- a/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml @@ -18,6 +18,7 @@ properties: - enum: - mediatek,mt2712-pwm - mediatek,mt6795-pwm + - mediatek,mt6991-pwm - mediatek,mt7622-pwm - mediatek,mt7623-pwm - mediatek,mt7628-pwm @@ -32,6 +33,10 @@ properties: - enum: - mediatek,mt8195-pwm - const: mediatek,mt8183-pwm + - items: + - enum: + - mediatek,mt8196-pwm + - const: mediatek,mt6991-pwm reg: maxItems: 1 -- GitLab From d4f1e7a2fe029ec7ca2c32ec10b58a84b56d719d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:17 +0200 Subject: [PATCH 438/868] pwm: pwm-mediatek: Pass PWM_CK_26M_SEL from platform data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for adding support for new SoCs, remove variable has_ck_26m_sel from pwm_mediatek_of_data and replace it with a u16 pwm_ck_26m_sel_reg, meant to hold the register offset for PWM_CK_26M_SEL. Also, since the reg offset is guaranteed to never be zero, the logic to check for "has_ck_26m_sel" is changed to check if the register offset in pwm_ck_26m_sel_reg is more than zero. Analogously, when writing, use the register offset from platform data instead of using the PWM_CK_26M_SEL definition. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20250623120118.109170-3-angelogioacchino.delregno@collabora.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-mediatek.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 33d3554b9197a..680ab2346c718 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -36,7 +36,7 @@ struct pwm_mediatek_of_data { unsigned int num_pwms; bool pwm45_fixup; - bool has_ck_26m_sel; + u16 pwm_ck_26m_sel_reg; const unsigned int *reg_offset; }; @@ -136,8 +136,8 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, } /* Make sure we use the bus clock and not the 26MHz clock */ - if (pc->soc->has_ck_26m_sel) - writel(0, pc->regs + PWM_CK_26M_SEL); + if (pc->soc->pwm_ck_26m_sel_reg) + writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg); /* Using resolution in picosecond gets accuracy higher */ resolution = (u64)NSEC_PER_SEC * 1000; @@ -294,84 +294,78 @@ static int pwm_mediatek_probe(struct platform_device *pdev) static const struct pwm_mediatek_of_data mt2712_pwm_data = { .num_pwms = 8, .pwm45_fixup = false, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt6795_pwm_data = { .num_pwms = 7, .pwm45_fixup = false, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7622_pwm_data = { .num_pwms = 6, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7623_pwm_data = { .num_pwms = 5, .pwm45_fixup = true, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7628_pwm_data = { .num_pwms = 4, .pwm45_fixup = true, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7629_pwm_data = { .num_pwms = 1, .pwm45_fixup = false, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7981_pwm_data = { .num_pwms = 3, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v2, }; static const struct pwm_mediatek_of_data mt7986_pwm_data = { .num_pwms = 2, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt7988_pwm_data = { .num_pwms = 8, .pwm45_fixup = false, - .has_ck_26m_sel = false, .reg_offset = mtk_pwm_reg_offset_v2, }; static const struct pwm_mediatek_of_data mt8183_pwm_data = { .num_pwms = 4, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt8365_pwm_data = { .num_pwms = 3, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v1, }; static const struct pwm_mediatek_of_data mt8516_pwm_data = { .num_pwms = 5, .pwm45_fixup = false, - .has_ck_26m_sel = true, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .reg_offset = mtk_pwm_reg_offset_v1, }; -- GitLab From e47026facf73a8431a4cdb90f11918c84af98597 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:18 +0200 Subject: [PATCH 439/868] pwm: pwm-mediatek: Add support for PWM IP V3.0.2 in MT6991/MT8196 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the PWM IP version 3.0.2, found in MediaTek's Dimensity 9400 MT6991 and in the MT8196 Chromebook SoC: this needs a new register offset array and also a different offset for the PWM_CK_26M_SEL register. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20250623120118.109170-4-angelogioacchino.delregno@collabora.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-mediatek.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 680ab2346c718..6777c511622aa 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -29,6 +29,7 @@ #define PWM45DWIDTH_FIXUP 0x30 #define PWMTHRES 0x30 #define PWM45THRES_FIXUP 0x34 +#define PWM_CK_26M_SEL_V3 0x74 #define PWM_CK_26M_SEL 0x210 #define PWM_CLK_DIV_MAX 7 @@ -64,6 +65,11 @@ static const unsigned int mtk_pwm_reg_offset_v2[] = { 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240 }; +/* PWM IP Version 3.0.2 */ +static const unsigned int mtk_pwm_reg_offset_v3[] = { + 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700, 0x0800 +}; + static inline struct pwm_mediatek_chip * to_pwm_mediatek_chip(struct pwm_chip *chip) { @@ -369,9 +375,17 @@ static const struct pwm_mediatek_of_data mt8516_pwm_data = { .reg_offset = mtk_pwm_reg_offset_v1, }; +static const struct pwm_mediatek_of_data mt6991_pwm_data = { + .num_pwms = 4, + .pwm45_fixup = false, + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL_V3, + .reg_offset = mtk_pwm_reg_offset_v3, +}; + static const struct of_device_id pwm_mediatek_of_match[] = { { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data }, { .compatible = "mediatek,mt6795-pwm", .data = &mt6795_pwm_data }, + { .compatible = "mediatek,mt6991-pwm", .data = &mt6991_pwm_data }, { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data }, { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, -- GitLab From 6fdd4d8c84f36c4814ec7d499e9fb88b170669c5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 30 Jun 2025 00:07:18 +0200 Subject: [PATCH 440/868] dt-bindings: vendor-prefixes: Document Argon40 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Argon 40 Technologies Limited is a SBC expansion board vendor. Document the prefix. For details see https://argon40.com . Acked-by: Conor Dooley Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20250629220757.936212-1-marek.vasut+renesas@mailbox.org Signed-off-by: Uwe Kleine-König --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 5d2a7a8d3ac6c..e5e5bd34136af 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -149,6 +149,8 @@ patternProperties: description: Arctic Sand "^arcx,.*": description: arcx Inc. / Archronix Inc. + "^argon40,.*": + description: Argon 40 Technologies Limited "^ariaboard,.*": description: Shanghai Novotech Co., Ltd. (Ariaboard) "^aries,.*": -- GitLab From f6bd99a2d24ecccb560771dde13eff2d0808d897 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 30 Jun 2025 00:07:19 +0200 Subject: [PATCH 441/868] dt-bindings: pwm: argon40,fan-hat: Document Argon40 Fan HAT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document trivial PWM on Argon40 Fan HAT, which is a RaspberryPi blower fan hat which can be controlled over I2C. Reviewed-by: Rob Herring (Arm) Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20250629220757.936212-2-marek.vasut+renesas@mailbox.org Signed-off-by: Uwe Kleine-König --- .../bindings/pwm/argon40,fan-hat.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml diff --git a/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml b/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml new file mode 100644 index 0000000000000..7dbc7c2cd8025 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/argon40,fan-hat.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Argon40 Fan HAT PWM controller + +maintainers: + - Marek Vasut + +description: + The trivial PWM on Argon40 Fan HAT, which is a RaspberryPi blower fan + hat which can be controlled over I2C, generates a fixed 30 kHz period + PWM signal with configurable 0..100% duty cycle to control the fan + speed. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: argon40,fan-hat + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pwm@1a { + compatible = "argon40,fan-hat"; + reg = <0x1a>; + #pwm-cells = <3>; + }; + }; -- GitLab From 0191c80e8a288d38f3f5a17dd0bb6a3b08c6e464 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 30 Jun 2025 00:07:20 +0200 Subject: [PATCH 442/868] pwm: argon-fan-hat: Add Argon40 Fan HAT support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add trivial PWM driver for Argon40 Fan HAT, which is a RaspberryPi blower fan hat which can be controlled over I2C. Model this device as a PWM, so the pwm-fan can be attached to it and handle thermal zones and RPM management in a generic manner. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20250629220757.936212-3-marek.vasut+renesas@mailbox.org Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 9 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-argon-fan-hat.c | 109 ++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 drivers/pwm/pwm-argon-fan-hat.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6e113f8b4baf2..3ef1757502ebd 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -66,6 +66,15 @@ config PWM_APPLE To compile this driver as a module, choose M here: the module will be called pwm-apple. +config PWM_ARGON_FAN_HAT + tristate "Argon40 Fan HAT support" + depends on I2C && OF + help + Generic PWM framework driver for Argon40 Fan HAT. + + To compile this driver as a module, choose M here: the module + will be called pwm-argon-fan-hat. + config PWM_ATMEL tristate "Atmel PWM support" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 96160f4257fcb..ff4f47e5fb7a0 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ADP5585) += pwm-adp5585.o obj-$(CONFIG_PWM_APPLE) += pwm-apple.o +obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o diff --git a/drivers/pwm/pwm-argon-fan-hat.c b/drivers/pwm/pwm-argon-fan-hat.c new file mode 100644 index 0000000000000..2c59bd142d40c --- /dev/null +++ b/drivers/pwm/pwm-argon-fan-hat.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Marek Vasut + * + * Limitations: + * - no support for offset/polarity + * - fixed 30 kHz period + * + * Argon Fan HAT https://argon40.com/products/argon-fan-hat + */ + +#include +#include +#include +#include + +#define ARGON40_FAN_HAT_PERIOD_NS 33333 /* ~30 kHz */ + +#define ARGON40_FAN_HAT_REG_DUTY_CYCLE 0x80 + +static int argon_fan_hat_round_waveform_tohw(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_waveform *wf, + void *_wfhw) +{ + u8 *wfhw = _wfhw; + + if (wf->duty_length_ns > ARGON40_FAN_HAT_PERIOD_NS) + *wfhw = 100; + else + *wfhw = mul_u64_u64_div_u64(wf->duty_length_ns, 100, ARGON40_FAN_HAT_PERIOD_NS); + + return 0; +} + +static int argon_fan_hat_round_waveform_fromhw(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw, + struct pwm_waveform *wf) +{ + const u8 *wfhw = _wfhw; + + wf->period_length_ns = ARGON40_FAN_HAT_PERIOD_NS; + wf->duty_length_ns = DIV64_U64_ROUND_UP(wf->period_length_ns * *wfhw, 100); + wf->duty_offset_ns = 0; + + return 0; +} + +static int argon_fan_hat_write_waveform(struct pwm_chip *chip, + struct pwm_device *pwm, + const void *_wfhw) +{ + struct i2c_client *i2c = pwmchip_get_drvdata(chip); + const u8 *wfhw = _wfhw; + + return i2c_smbus_write_byte_data(i2c, ARGON40_FAN_HAT_REG_DUTY_CYCLE, *wfhw); +} + +static const struct pwm_ops argon_fan_hat_pwm_ops = { + .sizeof_wfhw = sizeof(u8), + .round_waveform_fromhw = argon_fan_hat_round_waveform_fromhw, + .round_waveform_tohw = argon_fan_hat_round_waveform_tohw, + .write_waveform = argon_fan_hat_write_waveform, + /* + * The controller does not provide any way to read info back, + * reading from the controller stops the fan, therefore there + * is no .read_waveform here. + */ +}; + +static int argon_fan_hat_i2c_probe(struct i2c_client *i2c) +{ + struct pwm_chip *chip = devm_pwmchip_alloc(&i2c->dev, 1, 0); + int ret; + + if (IS_ERR(chip)) + return PTR_ERR(chip); + + chip->ops = &argon_fan_hat_pwm_ops; + pwmchip_set_drvdata(chip, i2c); + + ret = devm_pwmchip_add(&i2c->dev, chip); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Could not add PWM chip\n"); + + return 0; +} + +static const struct of_device_id argon_fan_hat_dt_ids[] = { + { .compatible = "argon40,fan-hat" }, + { }, +}; +MODULE_DEVICE_TABLE(of, argon_fan_hat_dt_ids); + +static struct i2c_driver argon_fan_hat_driver = { + .driver = { + .name = "argon-fan-hat", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = argon_fan_hat_dt_ids, + }, + .probe = argon_fan_hat_i2c_probe, +}; + +module_i2c_driver(argon_fan_hat_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("Argon40 Fan HAT"); +MODULE_LICENSE("GPL"); -- GitLab From 62df49917eb4aa378c21cb2f6a7093749870da96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:37 +0200 Subject: [PATCH 443/868] pwm: atmel: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two functions making use of the lock are only called transitively from .apply(). Calls to .apply() are already serialized by the pwm core so the lock in the driver has no effect and can safely be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/5ad3417aecd4dc6eca9699e21691e3725ea0bb87.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-atmel.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index b2f0abbbad630..06d22d0f7b261 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -91,9 +91,6 @@ struct atmel_pwm_chip { * hardware. */ u32 update_pending; - - /* Protects .update_pending */ - spinlock_t lock; }; static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip) @@ -145,8 +142,6 @@ static void atmel_pwm_update_pending(struct atmel_pwm_chip *chip) static void atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsigned int ch) { - spin_lock(&chip->lock); - /* * Clear pending flags in hardware because otherwise there might still * be a stale flag in ISR. @@ -154,16 +149,12 @@ static void atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsigned int ch) atmel_pwm_update_pending(chip); chip->update_pending |= (1 << ch); - - spin_unlock(&chip->lock); } static int atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsigned int ch) { int ret = 0; - spin_lock(&chip->lock); - if (chip->update_pending & (1 << ch)) { atmel_pwm_update_pending(chip); @@ -171,8 +162,6 @@ static int atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsigned int ch) ret = 1; } - spin_unlock(&chip->lock); - return ret; } @@ -509,7 +498,6 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->data = of_device_get_match_data(&pdev->dev); atmel_pwm->update_pending = 0; - spin_lock_init(&atmel_pwm->lock); atmel_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(atmel_pwm->base)) -- GitLab From f0d91b16dcb3d3daa013572caf76720fffcd7bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:38 +0200 Subject: [PATCH 444/868] pwm: clps711x: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core serializes calls to .apply(), so the spinlock adds no additional protection. Disabling the irq is also irrelevant as the driver isn't an atomic one and so the callbacks cannot be called from atomic context. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/d4931dc0c0d657d80722cfe7d97cb4fb4ccec90e.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-clps711x.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 04559a9de718b..2c92ce7548723 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -14,7 +14,6 @@ struct clps711x_chip { void __iomem *pmpcon; struct clk *clk; - spinlock_t lock; }; static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip) @@ -42,7 +41,6 @@ static int clps711x_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct clps711x_chip *priv = to_clps711x_chip(chip); /* PWM0 - bits 4..7, PWM1 - bits 8..11 */ u32 shift = (pwm->hwpwm + 1) * 4; - unsigned long flags; u32 pmpcon, val; if (state->polarity != PWM_POLARITY_NORMAL) @@ -56,15 +54,11 @@ static int clps711x_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, else val = 0; - spin_lock_irqsave(&priv->lock, flags); - pmpcon = readl(priv->pmpcon); pmpcon &= ~(0xf << shift); pmpcon |= val << shift; writel(pmpcon, priv->pmpcon); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; } @@ -93,8 +87,6 @@ static int clps711x_pwm_probe(struct platform_device *pdev) chip->ops = &clps711x_pwm_ops; - spin_lock_init(&priv->lock); - return devm_pwmchip_add(&pdev->dev, chip); } -- GitLab From 7c1a529a240b1c84204b9f45cc05f0db0fb17402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:39 +0200 Subject: [PATCH 445/868] pwm: fsl-ftm: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core serializes calls to .apply(), so the driver local lock isn't needed for that. It only has the effect to serialize .apply() with .request() and .free() for a different PWM, and .request() and .free against each other. But given that .request and .free() only do a single regmap operation under the lock and regmap itself serializes register accesses, it might happen without the lock that the calls are interleaved now, but affecting different PWMs, so nothing bad can happen. So the mutex has no effect and can be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/6b72104e5e1823170c7c9664189cc0f2ca5c2347.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-fsl-ftm.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index c45a5fca4cbbd..6683931872fc7 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -40,7 +39,6 @@ struct fsl_pwm_periodcfg { }; struct fsl_pwm_chip { - struct mutex lock; struct regmap *regmap; /* This value is valid iff a pwm is running */ @@ -89,11 +87,8 @@ static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) struct fsl_pwm_chip *fpc = to_fsl_chip(chip); ret = clk_prepare_enable(fpc->ipg_clk); - if (!ret && fpc->soc->has_enable_bits) { - mutex_lock(&fpc->lock); + if (!ret && fpc->soc->has_enable_bits) regmap_set_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); - mutex_unlock(&fpc->lock); - } return ret; } @@ -102,11 +97,8 @@ static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); - if (fpc->soc->has_enable_bits) { - mutex_lock(&fpc->lock); + if (fpc->soc->has_enable_bits) regmap_clear_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); - mutex_unlock(&fpc->lock); - } clk_disable_unprepare(fpc->ipg_clk); } @@ -304,7 +296,7 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); struct pwm_state *oldstate = &pwm->state; - int ret = 0; + int ret; /* * oldstate to newstate : action @@ -315,8 +307,6 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * disabled to enabled : update settings + enable */ - mutex_lock(&fpc->lock); - if (!newstate->enabled) { if (oldstate->enabled) { regmap_set_bits(fpc->regmap, FTM_OUTMASK, @@ -325,30 +315,28 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); } - goto end_mutex; + return 0; } ret = fsl_pwm_apply_config(chip, pwm, newstate); if (ret) - goto end_mutex; + return ret; /* check if need to enable */ if (!oldstate->enabled) { ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); if (ret) - goto end_mutex; + return ret; ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); if (ret) { clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); - goto end_mutex; + return ret; } regmap_clear_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm)); } -end_mutex: - mutex_unlock(&fpc->lock); return ret; } @@ -408,8 +396,6 @@ static int fsl_pwm_probe(struct platform_device *pdev) return PTR_ERR(chip); fpc = to_fsl_chip(chip); - mutex_init(&fpc->lock); - fpc->soc = of_device_get_match_data(&pdev->dev); base = devm_platform_ioremap_resource(pdev, 0); -- GitLab From 33d73bde06e9ab7862e8e8482ad1a9c4fa1a57f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:40 +0200 Subject: [PATCH 446/868] pwm: lpc18xx-sct: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both mutexes are only used in one function each. These functions are only called by the .apply() callback. As the .apply() calls are serialized by the core since commit 1cc2e1faafb3 ("pwm: Add more locking") the mutexes have no effect apart from runtime overhead. Drop them. Signed-off-by: Uwe Kleine-König Reviewed-by: Vladimir Zapolskiy Link: https://lore.kernel.org/r/4f7a2da37adbfe4743564245119045826d86eca6.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-lpc18xx-sct.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index f351baa63453f..1e614b2a02274 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -100,8 +100,6 @@ struct lpc18xx_pwm_chip { u64 max_period_ns; unsigned int period_event; unsigned long event_map; - struct mutex res_lock; - struct mutex period_lock; struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS]; }; @@ -129,8 +127,6 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, { u32 val; - mutex_lock(&lpc18xx_pwm->res_lock); - /* * Simultaneous set and clear may happen on an output, that is the case * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict @@ -140,8 +136,6 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm); val |= LPC18XX_PWM_RES(pwm->hwpwm, action); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val); - - mutex_unlock(&lpc18xx_pwm->res_lock); } static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns) @@ -200,8 +194,6 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -ERANGE; } - mutex_lock(&lpc18xx_pwm->period_lock); - requested_events = bitmap_weight(&lpc18xx_pwm->event_map, LPC18XX_PWM_EVENT_MAX); @@ -214,7 +206,6 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, lpc18xx_pwm->period_ns) { dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n", pwm->hwpwm); - mutex_unlock(&lpc18xx_pwm->period_lock); return -EBUSY; } @@ -224,8 +215,6 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, lpc18xx_pwm_config_period(chip, period_ns); } - mutex_unlock(&lpc18xx_pwm->period_lock); - lpc18xx_pwm_config_duty(chip, pwm, duty_ns); return 0; @@ -377,9 +366,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n"); - mutex_init(&lpc18xx_pwm->res_lock); - mutex_init(&lpc18xx_pwm->period_lock); - lpc18xx_pwm->max_period_ns = mul_u64_u64_div_u64(NSEC_PER_SEC, LPC18XX_PWM_TIMER_MAX, lpc18xx_pwm->clk_rate); -- GitLab From 9470e7d11fe2b6c21a35327175d51a06afd3fca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:41 +0200 Subject: [PATCH 447/868] pwm: microchip-core: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core already serializes .apply() and .get_state(), so the driver local lock is always free and adds no protection. Drop it. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/6d6ef0376ea0058b040eec3b257e324493a083f1.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-microchip-core.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c index 12821b4bbf975..4ff32bb4c2053 100644 --- a/drivers/pwm/pwm-microchip-core.c +++ b/drivers/pwm/pwm-microchip-core.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -56,7 +55,6 @@ struct mchp_core_pwm_chip { struct clk *clk; void __iomem *base; - struct mutex lock; /* protects the shared period */ ktime_t update_timestamp; u32 sync_update_mask; u16 channel_enabled; @@ -360,17 +358,10 @@ static int mchp_core_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); - int ret; - - mutex_lock(&mchp_core_pwm->lock); mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm); - ret = mchp_core_pwm_apply_locked(chip, pwm, state); - - mutex_unlock(&mchp_core_pwm->lock); - - return ret; + return mchp_core_pwm_apply_locked(chip, pwm, state); } static int mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, @@ -381,8 +372,6 @@ static int mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm u16 prescale, period_steps; u8 duty_steps, posedge, negedge; - mutex_lock(&mchp_core_pwm->lock); - mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm); if (mchp_core_pwm->channel_enabled & (1 << pwm->hwpwm)) @@ -415,8 +404,6 @@ static int mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm posedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm)); negedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm)); - mutex_unlock(&mchp_core_pwm->lock); - if (negedge == posedge) { state->duty_cycle = state->period; state->period *= 2; @@ -469,8 +456,6 @@ static int mchp_core_pwm_probe(struct platform_device *pdev) &mchp_core_pwm->sync_update_mask)) mchp_core_pwm->sync_update_mask = 0; - mutex_init(&mchp_core_pwm->lock); - chip->ops = &mchp_core_pwm_ops; mchp_core_pwm->channel_enabled = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_EN(0)); -- GitLab From d2c8bdc72fa906d78cac4f87ad9d75692fe47a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:42 +0200 Subject: [PATCH 448/868] pwm: sti: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core already serializes calls to .apply(), so the driver local mutex adds no protection and can be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/7ad150e40b45d6cb16fee633dcd6390a49a327a1.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sti.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 396b52672ce0b..3b702b8f0c7f0 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -92,7 +92,6 @@ struct sti_pwm_chip { struct pwm_device *cur; unsigned long configured; unsigned int en_count; - struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ void __iomem *mmio; }; @@ -244,55 +243,46 @@ static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct sti_pwm_chip *pc = to_sti_pwmchip(chip); struct device *dev = pc->dev; - int ret = 0; + int ret; /* * Since we have a common enable for all PWM devices, do not enable if * already enabled. */ - mutex_lock(&pc->sti_pwm_lock); if (!pc->en_count) { ret = clk_enable(pc->pwm_clk); if (ret) - goto out; + return ret; ret = clk_enable(pc->cpt_clk); if (ret) - goto out; + return ret; ret = regmap_field_write(pc->pwm_out_en, 1); if (ret) { dev_err(dev, "failed to enable PWM device %u: %d\n", pwm->hwpwm, ret); - goto out; + return ret; } } pc->en_count++; -out: - mutex_unlock(&pc->sti_pwm_lock); - return ret; + return 0; } static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct sti_pwm_chip *pc = to_sti_pwmchip(chip); - mutex_lock(&pc->sti_pwm_lock); - - if (--pc->en_count) { - mutex_unlock(&pc->sti_pwm_lock); + if (--pc->en_count) return; - } regmap_field_write(pc->pwm_out_en, 0); clk_disable(pc->pwm_clk); clk_disable(pc->cpt_clk); - - mutex_unlock(&pc->sti_pwm_lock); } static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) @@ -594,7 +584,6 @@ static int sti_pwm_probe(struct platform_device *pdev) pc->dev = dev; pc->en_count = 0; - mutex_init(&pc->sti_pwm_lock); ret = sti_pwm_probe_regmap(pc); if (ret) -- GitLab From dce0df8ac14fd2b4d00c17bc893fe3f3b06c853c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:43 +0200 Subject: [PATCH 449/868] pwm: sun4i: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core serializes calls to .apply(), so the driver lock doesn't add any protection and can safely be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/87b71c46b82b787959f0cea314d3010f16a50a29.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-sun4i.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index e60dc7d6b5915..6c5591ca868b4 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #define PWM_CTRL_REG 0x0 @@ -85,7 +84,6 @@ struct sun4i_pwm_chip { struct clk *clk; struct reset_control *rst; void __iomem *base; - spinlock_t ctrl_lock; const struct sun4i_pwm_data *data; }; @@ -258,7 +256,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } - spin_lock(&sun4ichip->ctrl_lock); ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); if (sun4ichip->data->has_direct_mod_clk_output) { @@ -266,7 +263,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); /* We can skip other parameter */ sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); - spin_unlock(&sun4ichip->ctrl_lock); return 0; } @@ -297,8 +293,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); - spin_unlock(&sun4ichip->ctrl_lock); - if (state->enabled) return 0; @@ -309,12 +303,10 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, else usleep_range(delay_us, delay_us * 2); - spin_lock(&sun4ichip->ctrl_lock); ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); - spin_unlock(&sun4ichip->ctrl_lock); clk_disable_unprepare(sun4ichip->clk); @@ -456,8 +448,6 @@ static int sun4i_pwm_probe(struct platform_device *pdev) chip->ops = &sun4i_pwm_ops; - spin_lock_init(&sun4ichip->ctrl_lock); - ret = pwmchip_add(chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); -- GitLab From 2c06a2178926993f77e52f77e6b0c540e3d771ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 20:15:44 +0200 Subject: [PATCH 450/868] pwm: twl-led: Drop driver local locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pwm core already serializes .apply(). twl6030's .request() and .free() are also already serialized against .apply() because there is only a single PWM. So the mutex doesn't add any additional protection and can be dropped. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/c1c7f646190f7cb2fe43b10959aa8dade80cb79e.1750788649.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-twl-led.c | 49 ++++++--------------------------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 4b10a8dab3124..a555cc3be4b31 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -61,10 +61,6 @@ #define TWL6040_LED_MODE_OFF 0x02 #define TWL6040_LED_MODE_MASK 0x03 -struct twl_pwmled_chip { - struct mutex mutex; -}; - static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) { return pwmchip_get_drvdata(chip); @@ -106,15 +102,13 @@ static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label); - goto out; + return ret; } val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); @@ -123,23 +117,19 @@ static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl4030_pwmled_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label); - goto out; + return; } val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); @@ -147,9 +137,6 @@ static void twl4030_pwmled_disable(struct pwm_chip *chip, ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); - -out: - mutex_unlock(&twl->mutex); } static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -209,16 +196,14 @@ static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return ret; } val &= ~TWL6040_LED_MODE_MASK; @@ -228,24 +213,20 @@ static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl6030_pwmled_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return; } val &= ~TWL6040_LED_MODE_MASK; @@ -254,9 +235,6 @@ static void twl6030_pwmled_disable(struct pwm_chip *chip, ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); - -out: - mutex_unlock(&twl->mutex); } static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -287,16 +265,14 @@ static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return ret; } val &= ~TWL6040_LED_MODE_MASK; @@ -306,23 +282,19 @@ static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return; } val &= ~TWL6040_LED_MODE_MASK; @@ -331,9 +303,6 @@ static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label); - -out: - mutex_unlock(&twl->mutex); } static const struct pwm_ops twl6030_pwmled_ops = { @@ -345,7 +314,6 @@ static const struct pwm_ops twl6030_pwmled_ops = { static int twl_pwmled_probe(struct platform_device *pdev) { struct pwm_chip *chip; - struct twl_pwmled_chip *twl; unsigned int npwm; const struct pwm_ops *ops; @@ -357,15 +325,12 @@ static int twl_pwmled_probe(struct platform_device *pdev) npwm = 1; } - chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*twl)); + chip = devm_pwmchip_alloc(&pdev->dev, npwm, 0); if (IS_ERR(chip)) return PTR_ERR(chip); - twl = to_twl(chip); chip->ops = ops; - mutex_init(&twl->mutex); - return devm_pwmchip_add(&pdev->dev, chip); } -- GitLab From 10e9b32d9a14edbbed902dd5447a1ca3cd487935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Jun 2025 12:05:00 +0200 Subject: [PATCH 451/868] docs: pwm: Adapt Locking paragraph to reality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have the distinction between pwm_apply_atomic() and pwm_apply_might_sleep() since commit c748a6d77c06 (pwm: Rename pwm_apply_state() to pwm_apply_might_sleep()) contained in v6.8-rc1. Locking in the core was introduced in commit 1cc2e1faafb3 ("pwm: Add more locking", contained in v6.13-rc1) to serialize per-chip callbacks and device removal. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250624100500.1429163-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- Documentation/driver-api/pwm.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index b41b1c56477f3..0d27a40f58187 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -173,10 +173,15 @@ Locking ------- The PWM core list manipulations are protected by a mutex, so pwm_get() -and pwm_put() may not be called from an atomic context. Currently the -PWM core does not enforce any locking to pwm_enable(), pwm_disable() and -pwm_config(), so the calling context is currently driver specific. This -is an issue derived from the former barebone API and should be fixed soon. +and pwm_put() may not be called from an atomic context. +Most functions in the PWM consumer API might sleep and so must not be called +from atomic context. The notable exception is pwm_apply_atomic() which has the +same semantics as pwm_apply_might_sleep() but can be called from atomic context. +(The price for that is that it doesn't work for all PWM devices, use +pwm_might_sleep() to check if a given PWM supports atomic operation. + +Locking in the PWM core ensures that callbacks related to a single chip are +serialized. Helpers ------- -- GitLab From 4cd2f417a0acdced4335fa19dc75b3a80941d60d Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 25 Jun 2025 12:19:08 -0400 Subject: [PATCH 452/868] dt-bindings: pwm: Convert lpc32xx-pwm.txt to yaml format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert pc32xx-pwm.txt to yaml format. Additional changes: - add ref to pwm.yaml - add clocks - restrict #pwm-cells to 3 Signed-off-by: Frank Li Reviewed-by: Vladimir Zapolskiy Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250625161909.2541315-1-Frank.Li@nxp.com Signed-off-by: Uwe Kleine-König --- .../devicetree/bindings/pwm/lpc32xx-pwm.txt | 17 ------- .../bindings/pwm/nxp,lpc3220-pwm.yaml | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 17 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt create mode 100644 Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt deleted file mode 100644 index 74b5bc5dd19ac..0000000000000 --- a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt +++ /dev/null @@ -1,17 +0,0 @@ -LPC32XX PWM controller - -Required properties: -- compatible: should be "nxp,lpc3220-pwm" -- reg: physical base address and length of the controller's registers - -Examples: - -pwm@4005c000 { - compatible = "nxp,lpc3220-pwm"; - reg = <0x4005c000 0x4>; -}; - -pwm@4005c004 { - compatible = "nxp,lpc3220-pwm"; - reg = <0x4005c004 0x4>; -}; diff --git a/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml b/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml new file mode 100644 index 0000000000000..d8ebb0735c96e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/nxp,lpc3220-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32XX PWM controller + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - nxp,lpc3220-pwm + - nxp,lpc3220-motor-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#pwm-cells': + const: 3 + +required: + - compatible + - reg + - '#pwm-cells' + +allOf: + - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: + - | + pwm@4005c000 { + compatible = "nxp,lpc3220-pwm"; + reg = <0x4005c000 0x4>; + #pwm-cells = <3>; + }; + -- GitLab From edd3bcb1801e1bb98f4f81485140e18c86406ced Mon Sep 17 00:00:00 2001 From: Michal Wilczynski Date: Wed, 2 Jul 2025 15:45:29 +0200 Subject: [PATCH 453/868] pwm: Expose PWM_WFHWSIZE in public header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WFHWSIZE constant defines the maximum size for the hardware-specific waveform representation buffer. It is currently local to drivers/pwm/core.c, which makes it inaccessible to external tools like bindgen. Move the constant to include/linux/pwm.h to make it part of the public API. As part of this change, rename it to PWM_WFHWSIZE to follow standard kernel conventions for namespacing macros in public headers. This allows bindgen to automatically generate a corresponding constant for the Rust PWM abstractions, ensuring the value remains synchronized between the C core and Rust code and preventing future maintenance issues. Signed-off-by: Michal Wilczynski Link: https://lore.kernel.org/r/20250702-rust-next-pwm-working-fan-for-sending-v7-1-67ef39ff1d29@samsung.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 26 ++++++++++++-------------- include/linux/pwm.h | 2 ++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 50aa0528a2653..0d66376a83ec3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -210,8 +210,6 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c return ret; } -#define WFHWSIZE 20 - /** * pwm_round_waveform_might_sleep - Query hardware capabilities * Cannot be used in atomic context. @@ -248,10 +246,10 @@ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform * struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; struct pwm_waveform wf_req = *wf; - char wfhw[WFHWSIZE]; + char wfhw[PWM_WFHWSIZE]; int ret_tohw, ret_fromhw; - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; @@ -306,10 +304,10 @@ int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf { struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; - char wfhw[WFHWSIZE]; + char wfhw[PWM_WFHWSIZE]; int err; - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip) || !ops->read_waveform) return -EOPNOTSUPP; @@ -334,11 +332,11 @@ static int __pwm_set_waveform(struct pwm_device *pwm, { struct pwm_chip *chip = pwm->chip; const struct pwm_ops *ops = chip->ops; - char wfhw[WFHWSIZE]; + char wfhw[PWM_WFHWSIZE]; struct pwm_waveform wf_rounded; int err, ret_tohw; - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); if (!pwmchip_supports_waveform(chip)) return -EOPNOTSUPP; @@ -650,9 +648,9 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) if (pwmchip_supports_waveform(chip)) { struct pwm_waveform wf; - char wfhw[WFHWSIZE]; + char wfhw[PWM_WFHWSIZE]; - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); pwm_state2wf(state, &wf); @@ -809,10 +807,10 @@ int pwm_get_state_hw(struct pwm_device *pwm, struct pwm_state *state) return -ENODEV; if (pwmchip_supports_waveform(chip) && ops->read_waveform) { - char wfhw[WFHWSIZE]; + char wfhw[PWM_WFHWSIZE]; struct pwm_waveform wf; - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); ret = __pwm_read_waveform(chip, pwm, &wfhw); if (ret) @@ -1696,8 +1694,8 @@ static bool pwm_ops_check(const struct pwm_chip *chip) !ops->write_waveform) return false; - if (WFHWSIZE < ops->sizeof_wfhw) { - dev_warn(pwmchip_parent(chip), "WFHWSIZE < %zu\n", ops->sizeof_wfhw); + if (PWM_WFHWSIZE < ops->sizeof_wfhw) { + dev_warn(pwmchip_parent(chip), "PWM_WFHWSIZE < %zu\n", ops->sizeof_wfhw); return false; } } else { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 2492c91452f96..8cafc483db53a 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -274,6 +274,8 @@ struct pwm_capture { unsigned int duty_cycle; }; +#define PWM_WFHWSIZE 20 + /** * struct pwm_ops - PWM controller operations * @request: optional hook for requesting a PWM -- GitLab From 527db0a8811697968df30797eae2e1d5f8b6964c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 23 Jun 2025 09:57:16 +0200 Subject: [PATCH 454/868] gpio: reg: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the legacy generic gpio-reg module to using them. We have to update the two legacy ARM platforms that use it at the same time as they call the set_multiple() callbacks directly (they shouldn't but it's old technical debt I suppose). Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250623-gpiochip-set-rv-gpio-v3-1-90f0e170a846@linaro.org Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-sa1100/assabet.c | 2 +- arch/arm/mach-sa1100/neponset.c | 2 +- drivers/gpio/gpio-reg.c | 16 ++++++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index 2b833aa0212b2..bad8aa661e9d0 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -80,7 +80,7 @@ void ASSABET_BCR_frob(unsigned int mask, unsigned int val) { unsigned long m = mask, v = val; - assabet_bcr_gc->set_multiple(assabet_bcr_gc, &m, &v); + assabet_bcr_gc->set_multiple_rv(assabet_bcr_gc, &m, &v); } EXPORT_SYMBOL(ASSABET_BCR_frob); diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c index 88fe79f0a4ed3..6516598c8a713 100644 --- a/arch/arm/mach-sa1100/neponset.c +++ b/arch/arm/mach-sa1100/neponset.c @@ -126,7 +126,7 @@ void neponset_ncr_frob(unsigned int mask, unsigned int val) unsigned long m = mask, v = val; if (nep) - n->gpio[0]->set_multiple(n->gpio[0], &m, &v); + n->gpio[0]->set_multiple_rv(n->gpio[0], &m, &v); else WARN(1, "nep unset\n"); } diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c index 73c7260d89c08..d8da99f973850 100644 --- a/drivers/gpio/gpio-reg.c +++ b/drivers/gpio/gpio-reg.c @@ -46,7 +46,7 @@ static int gpio_reg_direction_output(struct gpio_chip *gc, unsigned offset, if (r->direction & BIT(offset)) return -ENOTSUPP; - gc->set(gc, offset, value); + gc->set_rv(gc, offset, value); return 0; } @@ -57,7 +57,7 @@ static int gpio_reg_direction_input(struct gpio_chip *gc, unsigned offset) return r->direction & BIT(offset) ? 0 : -ENOTSUPP; } -static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) +static int gpio_reg_set(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; @@ -72,6 +72,8 @@ static void gpio_reg_set(struct gpio_chip *gc, unsigned offset, int value) r->out = val; writel_relaxed(val, r->reg); spin_unlock_irqrestore(&r->lock, flags); + + return 0; } static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) @@ -92,8 +94,8 @@ static int gpio_reg_get(struct gpio_chip *gc, unsigned offset) return !!(val & mask); } -static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { struct gpio_reg *r = to_gpio_reg(gc); unsigned long flags; @@ -102,6 +104,8 @@ static void gpio_reg_set_multiple(struct gpio_chip *gc, unsigned long *mask, r->out = (r->out & ~*mask) | (*bits & *mask); writel_relaxed(r->out, r->reg); spin_unlock_irqrestore(&r->lock, flags); + + return 0; } static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset) @@ -157,9 +161,9 @@ struct gpio_chip *gpio_reg_init(struct device *dev, void __iomem *reg, r->gc.get_direction = gpio_reg_get_direction; r->gc.direction_input = gpio_reg_direction_input; r->gc.direction_output = gpio_reg_direction_output; - r->gc.set = gpio_reg_set; + r->gc.set_rv = gpio_reg_set; r->gc.get = gpio_reg_get; - r->gc.set_multiple = gpio_reg_set_multiple; + r->gc.set_multiple_rv = gpio_reg_set_multiple; if (irqs) r->gc.to_irq = gpio_reg_to_irq; r->gc.base = base; -- GitLab From e567269e246809223fafaee7d421ae17a832fae5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:35 +0200 Subject: [PATCH 455/868] gpio: mmio: drop the big-endian platform device variant There are no more users of the "basic-mmio-gpio-be" platform device ID in the kernel. We can safely drop it. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-1-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 4841e4ebe7a67..5bb5112042149 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -809,9 +809,6 @@ static const struct platform_device_id bgpio_id_table[] = { { .name = "basic-mmio-gpio", .driver_data = 0, - }, { - .name = "basic-mmio-gpio-be", - .driver_data = BGPIOF_BIG_ENDIAN, }, { } }; -- GitLab From c4a834840596c8b9e388d430154959390f9f96e4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:36 +0200 Subject: [PATCH 456/868] gpio: mmio: get chip label and GPIO base from device properties Ahead of removing struct bgpio_pdata support from the gpio-mmio generic module, let's add support for getting the relevant values from generic device properties. "label" is a semi-standardized property in some GPIO drivers so let's go with it. There's no standard "base" property, so let's use the name "gpio-mmio,base" to tie it to this driver specifically. The number of GPIOs will be retrieved using gpiochip_get_ngpios() so there's no need to look it up in the software node. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-2-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 5bb5112042149..7d062f9a2c6a8 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -715,6 +715,9 @@ MODULE_DEVICE_TABLE(of, bgpio_of_match); static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags) { struct bgpio_pdata *pdata; + const char *label; + unsigned int base; + int ret; if (!dev_fwnode(dev)) return NULL; @@ -731,6 +734,18 @@ static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *fla if (device_property_read_bool(dev, "no-output")) *flags |= BGPIOF_NO_OUTPUT; + ret = device_property_read_string(dev, "label", &label); + if (!ret) + pdata->label = label; + + /* + * This property *must not* be used in device-tree sources, it's only + * meant to be passed to the driver from board files and MFD core. + */ + ret = device_property_read_u32(dev, "gpio-mmio,base", &base); + if (!ret && base <= INT_MAX) + pdata->base = base; + return pdata; } -- GitLab From 11cd2e582bf4da87b5fc0f9b07e194daaf212651 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:37 +0200 Subject: [PATCH 457/868] mfd: vexpress-sysreg: set-up software nodes for gpio-mmio Replace struct bgpio_pdata - that we plan to remove - with software nodes containing properties encoding the same values thatr can now be parsed by gpio-mmio. Acked-by: Lee Jones Reviewed-by: Liviu Dudau Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-3-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/mfd/vexpress-sysreg.c | 46 +++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index ef03d6cec9ff6..fc2daffc4352c 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -37,22 +38,34 @@ /* The sysreg block is just a random collection of various functions... */ -static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = { - .label = "sys_led", - .base = -1, - .ngpio = 8, +static const struct property_entry vexpress_sysreg_sys_led_props[] = { + PROPERTY_ENTRY_STRING("label", "sys_led"), + PROPERTY_ENTRY_U32("ngpios", 8), + { } }; -static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = { - .label = "sys_mci", - .base = -1, - .ngpio = 2, +static const struct software_node vexpress_sysreg_sys_led_swnode = { + .properties = vexpress_sysreg_sys_led_props, }; -static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = { - .label = "sys_flash", - .base = -1, - .ngpio = 1, +static const struct property_entry vexpress_sysreg_sys_mci_props[] = { + PROPERTY_ENTRY_STRING("label", "sys_mci"), + PROPERTY_ENTRY_U32("ngpios", 2), + { } +}; + +static const struct software_node vexpress_sysreg_sys_mci_swnode = { + .properties = vexpress_sysreg_sys_mci_props, +}; + +static const struct property_entry vexpress_sysreg_sys_flash_props[] = { + PROPERTY_ENTRY_STRING("label", "sys_flash"), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static const struct software_node vexpress_sysreg_sys_flash_swnode = { + .properties = vexpress_sysreg_sys_flash_props, }; static struct mfd_cell vexpress_sysreg_cells[] = { @@ -61,22 +74,19 @@ static struct mfd_cell vexpress_sysreg_cells[] = { .of_compatible = "arm,vexpress-sysreg,sys_led", .num_resources = 1, .resources = &DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"), - .platform_data = &vexpress_sysreg_sys_led_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata), + .swnode = &vexpress_sysreg_sys_led_swnode, }, { .name = "basic-mmio-gpio", .of_compatible = "arm,vexpress-sysreg,sys_mci", .num_resources = 1, .resources = &DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"), - .platform_data = &vexpress_sysreg_sys_mci_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata), + .swnode = &vexpress_sysreg_sys_mci_swnode, }, { .name = "basic-mmio-gpio", .of_compatible = "arm,vexpress-sysreg,sys_flash", .num_resources = 1, .resources = &DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"), - .platform_data = &vexpress_sysreg_sys_flash_pdata, - .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata), + .swnode = &vexpress_sysreg_sys_flash_swnode, }, { .name = "vexpress-syscfg", .num_resources = 1, -- GitLab From 094017efe332d411a8d6ac41fd8d0a4f81f72a99 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:38 +0200 Subject: [PATCH 458/868] ARM: omap1: ams-delta: use generic device properties for gpio-mmio The two latch GPIO devices in ams-delta are registered with struct bgpio_pdata passed as platform_data to the gpio-mmio driver. We want to remove the bgpio_pdata from the kernel and the gpio-mmio driver is now also able to get the relevant values from the software node. Set up device properties and switch to using platform_device_info to register the devices as platform_add_devices() doesn't allow us to pass device properties to the driver model. Reviewed-by: Linus Walleij Acked-by: Janusz Krzysztofik Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-4-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-omap1/board-ams-delta.c | 42 +++++++++++++-------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index 0daf6c5b5c1cb..16392720296cd 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -175,20 +176,18 @@ static struct resource latch1_resources[] = { #define LATCH1_LABEL "latch1" -static struct bgpio_pdata latch1_pdata = { - .label = LATCH1_LABEL, - .base = -1, - .ngpio = LATCH1_NGPIO, +static const struct property_entry latch1_gpio_props[] = { + PROPERTY_ENTRY_STRING("label", LATCH1_LABEL), + PROPERTY_ENTRY_U32("ngpios", LATCH1_NGPIO), + { } }; -static struct platform_device latch1_gpio_device = { +static const struct platform_device_info latch1_gpio_devinfo = { .name = "basic-mmio-gpio", .id = 0, - .resource = latch1_resources, - .num_resources = ARRAY_SIZE(latch1_resources), - .dev = { - .platform_data = &latch1_pdata, - }, + .res = latch1_resources, + .num_res = ARRAY_SIZE(latch1_resources), + .properties = latch1_gpio_props, }; #define LATCH1_PIN_LED_CAMERA 0 @@ -213,20 +212,18 @@ static struct resource latch2_resources[] = { #define LATCH2_LABEL "latch2" -static struct bgpio_pdata latch2_pdata = { - .label = LATCH2_LABEL, - .base = -1, - .ngpio = LATCH2_NGPIO, +static const struct property_entry latch2_gpio_props[] = { + PROPERTY_ENTRY_STRING("label", LATCH2_LABEL), + PROPERTY_ENTRY_U32("ngpios", LATCH2_NGPIO), + { } }; -static struct platform_device latch2_gpio_device = { +static struct platform_device_info latch2_gpio_devinfo = { .name = "basic-mmio-gpio", .id = 1, - .resource = latch2_resources, - .num_resources = ARRAY_SIZE(latch2_resources), - .dev = { - .platform_data = &latch2_pdata, - }, + .res = latch2_resources, + .num_res = ARRAY_SIZE(latch2_resources), + .properties = latch2_gpio_props, }; #define LATCH2_PIN_LCD_VBLEN 0 @@ -542,8 +539,6 @@ static struct gpiod_lookup_table keybrd_pwr_gpio_table = { }; static struct platform_device *ams_delta_devices[] __initdata = { - &latch1_gpio_device, - &latch2_gpio_device, &ams_delta_kp_device, &ams_delta_audio_device, &ams_delta_serio_device, @@ -697,6 +692,9 @@ static void __init ams_delta_init(void) omap1_usb_init(&ams_delta_usb_config); platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices)); + platform_device_register_full(&latch1_gpio_devinfo); + platform_device_register_full(&latch2_gpio_devinfo); + /* * As soon as regulator consumers have been registered, assign their * dev_names to consumer supply entries of respective regulators. -- GitLab From bb9c88d5b0fabe05b0ed4b843efe78ac1c4712f0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:39 +0200 Subject: [PATCH 459/868] ARM: s3c: crag6410: use generic device properties for gpio-mmio The GPIO device in crag6410 is registered with struct bgpio_pdata passed as platform_data to the gpio-mmio driver. We want to remove the bgpio_pdata from the kernel and the gpio-mmio driver is now also able to get the relevant values from the software node. Set up device properties and switch to using platform_device_info to register the device as platform_add_devices() doesn't allow us to pass device properties to the driver model. Acked-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-5-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- arch/arm/mach-s3c/mach-crag6410.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-s3c/mach-crag6410.c b/arch/arm/mach-s3c/mach-crag6410.c index e5df2cb51ab27..028169c7debf3 100644 --- a/arch/arm/mach-s3c/mach-crag6410.c +++ b/arch/arm/mach-s3c/mach-crag6410.c @@ -252,14 +252,17 @@ static struct resource crag6410_mmgpio_resource[] = { [0] = DEFINE_RES_MEM_NAMED(S3C64XX_PA_XM0CSN4, 1, "dat"), }; -static struct platform_device crag6410_mmgpio = { +static const struct property_entry crag6410_mmgpio_props[] = { + PROPERTY_ENTRY_U32("gpio-mmio,base", MMGPIO_GPIO_BASE), + { } +}; + +static struct platform_device_info crag6410_mmgpio_devinfo = { .name = "basic-mmio-gpio", .id = -1, - .resource = crag6410_mmgpio_resource, - .num_resources = ARRAY_SIZE(crag6410_mmgpio_resource), - .dev.platform_data = &(struct bgpio_pdata) { - .base = MMGPIO_GPIO_BASE, - }, + .res = crag6410_mmgpio_resource, + .num_res = ARRAY_SIZE(crag6410_mmgpio_resource), + .properties = crag6410_mmgpio_props, }; static struct platform_device speyside_device = { @@ -373,7 +376,6 @@ static struct platform_device *crag6410_devices[] __initdata = { &crag6410_gpio_keydev, &crag6410_dm9k_device, &s3c64xx_device_spi0, - &crag6410_mmgpio, &crag6410_lcd_powerdev, &crag6410_backlight_device, &speyside_device, @@ -871,6 +873,7 @@ static void __init crag6410_machine_init(void) pwm_add_table(crag6410_pwm_lookup, ARRAY_SIZE(crag6410_pwm_lookup)); platform_add_devices(crag6410_devices, ARRAY_SIZE(crag6410_devices)); + platform_device_register_full(&crag6410_mmgpio_devinfo); gpio_led_register_device(-1, &gpio_leds_pdata); -- GitLab From 9bad4bec5daddbb296481af759f9d56c849ba96f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Jul 2025 13:49:40 +0200 Subject: [PATCH 460/868] gpio: mmio: remove struct bgpio_pdata With no more users, we can now remove struct bgpio_pdata. Move the relevant bits from bgpio_parse_fw() into bgpio_pdev_probe() while maintaining the logical ordering (get flags before calling bgpio_init()). Link: https://lore.kernel.org/r/20250701-gpio-mmio-pdata-v2-6-ebf34d273497@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 73 ++++++++++--------------------------- include/linux/gpio/driver.h | 6 --- 2 files changed, 19 insertions(+), 60 deletions(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 7d062f9a2c6a8..a8103e26298c9 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -712,43 +712,6 @@ static const struct of_device_id bgpio_of_match[] = { }; MODULE_DEVICE_TABLE(of, bgpio_of_match); -static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags) -{ - struct bgpio_pdata *pdata; - const char *label; - unsigned int base; - int ret; - - if (!dev_fwnode(dev)) - return NULL; - - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->base = -1; - - if (device_is_big_endian(dev)) - *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; - - if (device_property_read_bool(dev, "no-output")) - *flags |= BGPIOF_NO_OUTPUT; - - ret = device_property_read_string(dev, "label", &label); - if (!ret) - pdata->label = label; - - /* - * This property *must not* be used in device-tree sources, it's only - * meant to be passed to the driver from board files and MFD core. - */ - ret = device_property_read_u32(dev, "gpio-mmio,base", &base); - if (!ret && base <= INT_MAX) - pdata->base = base; - - return pdata; -} - static int bgpio_pdev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -760,18 +723,10 @@ static int bgpio_pdev_probe(struct platform_device *pdev) void __iomem *dirin; unsigned long sz; unsigned long flags = 0; + unsigned int base; int err; struct gpio_chip *gc; - struct bgpio_pdata *pdata; - - pdata = bgpio_parse_fw(dev, &flags); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - - if (!pdata) { - pdata = dev_get_platdata(dev); - flags = pdev->id_entry->driver_data; - } + const char *label; r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); if (!r) @@ -803,17 +758,27 @@ static int bgpio_pdev_probe(struct platform_device *pdev) if (!gc) return -ENOMEM; + if (device_is_big_endian(dev)) + flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + + if (device_property_read_bool(dev, "no-output")) + flags |= BGPIOF_NO_OUTPUT; + err = bgpio_init(gc, dev, sz, dat, set, clr, dirout, dirin, flags); if (err) return err; - if (pdata) { - if (pdata->label) - gc->label = pdata->label; - gc->base = pdata->base; - if (pdata->ngpio > 0) - gc->ngpio = pdata->ngpio; - } + err = device_property_read_string(dev, "label", &label); + if (!err) + gc->label = label; + + /* + * This property *must not* be used in device-tree sources, it's only + * meant to be passed to the driver from board files and MFD core. + */ + err = device_property_read_u32(dev, "gpio-mmio,base", &base); + if (!err && base <= INT_MAX) + gc->base = base; platform_set_drvdata(pdev, gc); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index b53233051beed..602d4acd36b25 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -718,12 +718,6 @@ const unsigned long *gpiochip_query_valid_mask(const struct gpio_chip *gc); /* get driver data */ void *gpiochip_get_data(struct gpio_chip *gc); -struct bgpio_pdata { - const char *label; - int base; - int ngpio; -}; - #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY int gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc, -- GitLab From 8595375e4fded27de24b189c692c2c50051a7b3b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:08 +0200 Subject: [PATCH 461/868] gpio: generic: add new generic GPIO chip API As the first step in removing the fields specific to the gpio-mmio module from struct gpio_chip, we introduce a new set of generic GPIO chip interfaces that are meant to replace the existing bgpio_ ones. The new initialization function - gpio_generic_chip_init() - takes a configuration structure as argument instead of 9 separate parameters. This will allow easy extension if needed in the future. We hide the locking details behind a set of helpers in order to be able to move the raw spinlock out of struct gpio_chip without the users noticing. For now, the new APIs just wrap the existing ones. Once all users have been converted to the new interfaces, we'll pull them into gpio-mmio and implement them in a backward-compatible way while also moving all fields specific to the generic GPIO chip into struct gpio_generic_chip. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-1-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/generic.h | 120 +++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 include/linux/gpio/generic.h diff --git a/include/linux/gpio/generic.h b/include/linux/gpio/generic.h new file mode 100644 index 0000000000000..b511acd58ab00 --- /dev/null +++ b/include/linux/gpio/generic.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_GPIO_GENERIC_H +#define __LINUX_GPIO_GENERIC_H + +#include +#include +#include + +struct device; + +/** + * struct gpio_generic_chip_config - Generic GPIO chip configuration data + * @dev: Parent device of the new GPIO chip (compulsory). + * @sz: Size (width) of the MMIO registers in bytes, typically 1, 2 or 4. + * @dat: MMIO address for the register to READ the value of the GPIO lines, it + * is expected that a 1 in the corresponding bit in this register means + * the line is asserted. + * @set: MMIO address for the register to SET the value of the GPIO lines, it + * is expected that we write the line with 1 in this register to drive + * the GPIO line high. + * @clr: MMIO address for the register to CLEAR the value of the GPIO lines, + * it is expected that we write the line with 1 in this register to + * drive the GPIO line low. It is allowed to leave this address as NULL, + * in that case the SET register will be assumed to also clear the GPIO + * lines, by actively writing the line with 0. + * @dirout: MMIO address for the register to set the line as OUTPUT. It is + * assumed that setting a line to 1 in this register will turn that + * line into an output line. Conversely, setting the line to 0 will + * turn that line into an input. + * @dirin: MMIO address for the register to set this line as INPUT. It is + * assumed that setting a line to 1 in this register will turn that + * line into an input line. Conversely, setting the line to 0 will + * turn that line into an output. + * @flags: Different flags that will affect the behaviour of the device, such + * as endianness etc. + */ +struct gpio_generic_chip_config { + struct device *dev; + unsigned long sz; + void __iomem *dat; + void __iomem *set; + void __iomem *clr; + void __iomem *dirout; + void __iomem *dirin; + unsigned long flags; +}; + +/** + * struct gpio_generic_chip - Generic GPIO chip implementation. + * @gc: The underlying struct gpio_chip object, implementing low-level GPIO + * chip routines. + */ +struct gpio_generic_chip { + struct gpio_chip gc; +}; + +/** + * gpio_generic_chip_init() - Initialize a generic GPIO chip. + * @chip: Generic GPIO chip to set up. + * @cfg: Generic GPIO chip configuration. + * + * Returns 0 on success, negative error number on failure. + */ +static inline int +gpio_generic_chip_init(struct gpio_generic_chip *chip, + const struct gpio_generic_chip_config *cfg) +{ + return bgpio_init(&chip->gc, cfg->dev, cfg->sz, cfg->dat, cfg->set, + cfg->clr, cfg->dirout, cfg->dirin, cfg->flags); +} + +/** + * gpio_generic_chip_set() - Set the GPIO line value of the generic GPIO chip. + * @chip: Generic GPIO chip to use. + * @offset: Hardware offset of the line to set. + * @value: New GPIO line value. + * + * Some modules using the generic GPIO chip, need to set line values in their + * direction setters but they don't have access to the gpio-mmio symbols so + * they use the function pointer in struct gpio_chip directly. This is not + * optimal and can lead to crashes at run-time in some instances. This wrapper + * provides a safe interface for users. + * + * Returns: 0 on success, negative error number of failure. + */ +static inline int +gpio_generic_chip_set(struct gpio_generic_chip *chip, unsigned int offset, + int value) +{ + if (WARN_ON(!chip->gc.set_rv)) + return -EOPNOTSUPP; + + return chip->gc.set_rv(&chip->gc, offset, value); +} + +#define gpio_generic_chip_lock(gen_gc) \ + raw_spin_lock(&(gen_gc)->gc.bgpio_lock) + +#define gpio_generic_chip_unlock(gen_gc) \ + raw_spin_unlock(&(gen_gc)->gc.bgpio_lock) + +#define gpio_generic_chip_lock_irqsave(gen_gc, flags) \ + raw_spin_lock_irqsave(&(gen_gc)->gc.bgpio_lock, flags) + +#define gpio_generic_chip_unlock_irqrestore(gen_gc, flags) \ + raw_spin_unlock_irqrestore(&(gen_gc)->gc.bgpio_lock, flags) + +DEFINE_LOCK_GUARD_1(gpio_generic_lock, + struct gpio_generic_chip, + gpio_generic_chip_lock(_T->lock), + gpio_generic_chip_unlock(_T->lock)) + +DEFINE_LOCK_GUARD_1(gpio_generic_lock_irqsave, + struct gpio_generic_chip, + gpio_generic_chip_lock_irqsave(_T->lock, _T->flags), + gpio_generic_chip_unlock_irqrestore(_T->lock, _T->flags), + unsigned long flags) + +#endif /* __LINUX_GPIO_GENERIC_H */ -- GitLab From ba441322c7aac4735d78fc6781e497a01fb8eac7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:09 +0200 Subject: [PATCH 462/868] gpio: mxc: use lock guards for the generic GPIO chip lock Simplify the code by using lock guards for the bgpio_lock. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-2-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 48 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 4af5a2972d12f..1c37168c8d0a6 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -7,6 +7,7 @@ // Authors: Daniel Mack, Juergen Beisert. // Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. +#include #include #include #include @@ -161,7 +162,6 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxc_gpio_port *port = gc->private; - unsigned long flags; u32 bit, val; u32 gpio_idx = d->hwirq; int edge; @@ -200,41 +200,38 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) return -EINVAL; } - raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); + scoped_guard(raw_spinlock_irqsave, &port->gc.bgpio_lock) { + if (GPIO_EDGE_SEL >= 0) { + val = readl(port->base + GPIO_EDGE_SEL); + if (edge == GPIO_INT_BOTH_EDGES) + writel(val | (1 << gpio_idx), + port->base + GPIO_EDGE_SEL); + else + writel(val & ~(1 << gpio_idx), + port->base + GPIO_EDGE_SEL); + } - if (GPIO_EDGE_SEL >= 0) { - val = readl(port->base + GPIO_EDGE_SEL); - if (edge == GPIO_INT_BOTH_EDGES) - writel(val | (1 << gpio_idx), - port->base + GPIO_EDGE_SEL); - else - writel(val & ~(1 << gpio_idx), - port->base + GPIO_EDGE_SEL); - } + if (edge != GPIO_INT_BOTH_EDGES) { + reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ + bit = gpio_idx & 0xf; + val = readl(reg) & ~(0x3 << (bit << 1)); + writel(val | (edge << (bit << 1)), reg); + } - if (edge != GPIO_INT_BOTH_EDGES) { - reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */ - bit = gpio_idx & 0xf; - val = readl(reg) & ~(0x3 << (bit << 1)); - writel(val | (edge << (bit << 1)), reg); + writel(1 << gpio_idx, port->base + GPIO_ISR); + port->pad_type[gpio_idx] = type; } - writel(1 << gpio_idx, port->base + GPIO_ISR); - port->pad_type[gpio_idx] = type; - - raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); - return port->gc.direction_input(&port->gc, gpio_idx); } static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) { void __iomem *reg = port->base; - unsigned long flags; u32 bit, val; int edge; - raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags); + guard(raw_spinlock_irqsave)(&port->gc.bgpio_lock); reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ bit = gpio & 0xf; @@ -250,12 +247,9 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) } else { pr_err("mxc: invalid configuration for GPIO %d: %x\n", gpio, edge); - goto unlock; + return; } writel(val | (edge << (bit << 1)), reg); - -unlock: - raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); } /* handle 32 interrupts in one status register */ -- GitLab From 1f129b15c2dea84ce12ee3120c7fffdb7bfc7395 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:10 +0200 Subject: [PATCH 463/868] gpio: mxc: use new generic GPIO chip API Convert the driver to using the new generic GPIO chip interfaces from linux/gpio/generic.h. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-3-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mxc.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 1c37168c8d0a6..433cbadc3a4cc 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,7 @@ struct mxc_gpio_port { int irq_high; void (*mx_irq_handler)(struct irq_desc *desc); struct irq_domain *domain; - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; struct device *dev; u32 both_edges; struct mxc_gpio_reg_saved gpio_saved_reg; @@ -179,7 +180,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) if (GPIO_EDGE_SEL >= 0) { edge = GPIO_INT_BOTH_EDGES; } else { - val = port->gc.get(&port->gc, gpio_idx); + val = port->gen_gc.gc.get(&port->gen_gc.gc, gpio_idx); if (val) { edge = GPIO_INT_LOW_LEV; pr_debug("mxc: set GPIO %d to low trigger\n", gpio_idx); @@ -200,7 +201,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) return -EINVAL; } - scoped_guard(raw_spinlock_irqsave, &port->gc.bgpio_lock) { + scoped_guard(gpio_generic_lock_irqsave, &port->gen_gc) { if (GPIO_EDGE_SEL >= 0) { val = readl(port->base + GPIO_EDGE_SEL); if (edge == GPIO_INT_BOTH_EDGES) @@ -222,7 +223,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) port->pad_type[gpio_idx] = type; } - return port->gc.direction_input(&port->gc, gpio_idx); + return port->gen_gc.gc.direction_input(&port->gen_gc.gc, gpio_idx); } static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) @@ -231,7 +232,7 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) u32 bit, val; int edge; - guard(raw_spinlock_irqsave)(&port->gc.bgpio_lock); + guard(gpio_generic_lock_irqsave)(&port->gen_gc); reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ bit = gpio & 0xf; @@ -414,6 +415,7 @@ static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enab static int mxc_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; int irq_count; @@ -473,27 +475,31 @@ static int mxc_gpio_probe(struct platform_device *pdev) port->mx_irq_handler = mx3_gpio_irq_handler; mxc_update_irq_chained_handler(port, true); - err = bgpio_init(&port->gc, &pdev->dev, 4, - port->base + GPIO_PSR, - port->base + GPIO_DR, NULL, - port->base + GPIO_GDIR, NULL, - BGPIOF_READ_OUTPUT_REG_SET); + + config.dev = &pdev->dev; + config.sz = 4; + config.dat = port->base + GPIO_PSR; + config.set = port->base + GPIO_DR; + config.dirout = port->base + GPIO_GDIR; + config.flags = BGPIOF_READ_OUTPUT_REG_SET; + + err = gpio_generic_chip_init(&port->gen_gc, &config); if (err) goto out_bgio; - port->gc.request = mxc_gpio_request; - port->gc.free = mxc_gpio_free; - port->gc.to_irq = mxc_gpio_to_irq; + port->gen_gc.gc.request = mxc_gpio_request; + port->gen_gc.gc.free = mxc_gpio_free; + port->gen_gc.gc.to_irq = mxc_gpio_to_irq; /* * Driver is DT-only, so a fixed base needs only be maintained for legacy * userspace with sysfs interface. */ if (IS_ENABLED(CONFIG_GPIO_SYSFS)) - port->gc.base = of_alias_get_id(np, "gpio") * 32; + port->gen_gc.gc.base = of_alias_get_id(np, "gpio") * 32; else /* silence boot time warning */ - port->gc.base = -1; + port->gen_gc.gc.base = -1; - err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); + err = devm_gpiochip_add_data(&pdev->dev, &port->gen_gc.gc, port); if (err) goto out_bgio; @@ -567,7 +573,8 @@ static bool mxc_gpio_generic_config(struct mxc_gpio_port *port, if (of_device_is_compatible(np, "fsl,imx8dxl-gpio") || of_device_is_compatible(np, "fsl,imx8qxp-gpio") || of_device_is_compatible(np, "fsl,imx8qm-gpio")) - return (gpiochip_generic_config(&port->gc, offset, conf) == 0); + return (gpiochip_generic_config(&port->gen_gc.gc, + offset, conf) == 0); return false; } -- GitLab From fd0f0d1a1e71d736deed3593470b3b03f8e76df7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:11 +0200 Subject: [PATCH 464/868] gpio: clps711x: use new generic GPIO chip API Convert the driver to using the new generic GPIO chip interfaces from linux/gpio/generic.h. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-4-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-clps711x.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index c0c8d29d0dce7..24ff2347d5997 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -8,13 +8,15 @@ #include #include #include +#include #include static int clps711x_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device_node *np = pdev->dev.of_node; + struct gpio_generic_chip *gen_gc; void __iomem *dat, *dir; - struct gpio_chip *gc; int err, id; if (!np) @@ -24,8 +26,8 @@ static int clps711x_gpio_probe(struct platform_device *pdev) if ((id < 0) || (id > 4)) return -ENODEV; - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); - if (!gc) + gen_gc = devm_kzalloc(&pdev->dev, sizeof(*gen_gc), GFP_KERNEL); + if (!gen_gc) return -ENOMEM; dat = devm_platform_ioremap_resource(pdev, 0); @@ -36,34 +38,37 @@ static int clps711x_gpio_probe(struct platform_device *pdev) if (IS_ERR(dir)) return PTR_ERR(dir); + config.dev = &pdev->dev; + config.sz = 1; + config.dat = dat; + switch (id) { case 3: /* PORTD is inverted logic for direction register */ - err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, - NULL, dir, 0); + config.dirin = dir; break; default: - err = bgpio_init(gc, &pdev->dev, 1, dat, NULL, NULL, - dir, NULL, 0); + config.dirout = dir; break; } + err = gpio_generic_chip_init(gen_gc, &config); if (err) return err; switch (id) { case 4: /* PORTE is 3 lines only */ - gc->ngpio = 3; + gen_gc->gc.ngpio = 3; break; default: break; } - gc->base = -1; - gc->owner = THIS_MODULE; + gen_gc->gc.base = -1; + gen_gc->gc.owner = THIS_MODULE; - return devm_gpiochip_add_data(&pdev->dev, gc, NULL); + return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL); } static const struct of_device_id clps711x_gpio_ids[] = { -- GitLab From 76045e90400b7ecc60a33526a505124b0cce1d7a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:12 +0200 Subject: [PATCH 465/868] gpio: cadence: use lock guards Simplify the code by using lock guards for the bgpio_lock. While at it: move the gpio/driver.h include into its correct place alphabetically. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-5-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cadence.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index e9dd2564c54f8..e6ec341d55e90 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -8,8 +8,9 @@ * Boris Brezillon */ -#include +#include #include +#include #include #include #include @@ -38,29 +39,24 @@ struct cdns_gpio_chip { static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(raw_spinlock)(&chip->bgpio_lock); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset), cgpio->regs + CDNS_GPIO_BYPASS_MODE); - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); return 0; } static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(raw_spinlock)(&chip->bgpio_lock); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) | (BIT(offset) & cgpio->bypass_orig), cgpio->regs + CDNS_GPIO_BYPASS_MODE); - - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); } static void cdns_gpio_irq_mask(struct irq_data *d) @@ -85,13 +81,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) { struct gpio_chip *chip = irq_data_get_irq_chip_data(d); struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - unsigned long flags; u32 int_value; u32 int_type; u32 mask = BIT(d->hwirq); int ret = 0; - raw_spin_lock_irqsave(&chip->bgpio_lock, flags); + guard(raw_spinlock)(&chip->bgpio_lock); int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask; int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask; @@ -108,15 +103,12 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) } else if (type == IRQ_TYPE_LEVEL_LOW) { int_type |= mask; } else { - ret = -EINVAL; - goto err_irq_type; + return -EINVAL; } iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE); iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE); -err_irq_type: - raw_spin_unlock_irqrestore(&chip->bgpio_lock, flags); return ret; } -- GitLab From 47ecff3839cac71c6b7f89b7860f6f76a4c07b65 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:13 +0200 Subject: [PATCH 466/868] gpio: cadence: use new generic GPIO chip API Convert the driver to using the new generic GPIO chip interfaces from linux/gpio/generic.h. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-6-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cadence.c | 46 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index e6ec341d55e90..8243eddcd5bbe 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,7 +33,7 @@ #define CDNS_GPIO_IRQ_ANY_EDGE 0x2c struct cdns_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; void __iomem *regs; u32 bypass_orig; }; @@ -40,7 +42,7 @@ static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - guard(raw_spinlock)(&chip->bgpio_lock); + guard(gpio_generic_lock)(&cgpio->gen_gc); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset), cgpio->regs + CDNS_GPIO_BYPASS_MODE); @@ -52,7 +54,7 @@ static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip); - guard(raw_spinlock)(&chip->bgpio_lock); + guard(gpio_generic_lock)(&cgpio->gen_gc); iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) | (BIT(offset) & cgpio->bypass_orig), @@ -86,7 +88,7 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type) u32 mask = BIT(d->hwirq); int ret = 0; - guard(raw_spinlock)(&chip->bgpio_lock); + guard(gpio_generic_lock)(&cgpio->gen_gc); int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask; int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask; @@ -142,6 +144,7 @@ static const struct irq_chip cdns_gpio_irqchip = { static int cdns_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct cdns_gpio_chip *cgpio; int ret, irq; u32 dir_prev; @@ -168,32 +171,33 @@ static int cdns_gpio_probe(struct platform_device *pdev) * gpiochip_lock_as_irq: * tried to flag a GPIO set as output for IRQ * Generic GPIO driver stores the direction value internally, - * so it needs to be changed before bgpio_init() is called. + * so it needs to be changed before gpio_generic_chip_init() is called. */ dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE); iowrite32(GENMASK(num_gpios - 1, 0), cgpio->regs + CDNS_GPIO_DIRECTION_MODE); - ret = bgpio_init(&cgpio->gc, &pdev->dev, 4, - cgpio->regs + CDNS_GPIO_INPUT_VALUE, - cgpio->regs + CDNS_GPIO_OUTPUT_VALUE, - NULL, - NULL, - cgpio->regs + CDNS_GPIO_DIRECTION_MODE, - BGPIOF_READ_OUTPUT_REG_SET); + config.dev = &pdev->dev; + config.sz = 4; + config.dat = cgpio->regs + CDNS_GPIO_INPUT_VALUE; + config.set = cgpio->regs + CDNS_GPIO_OUTPUT_VALUE; + config.dirin = cgpio->regs + CDNS_GPIO_DIRECTION_MODE; + config.flags = BGPIOF_READ_OUTPUT_REG_SET; + + ret = gpio_generic_chip_init(&cgpio->gen_gc, &config); if (ret) { dev_err(&pdev->dev, "Failed to register generic gpio, %d\n", ret); goto err_revert_dir; } - cgpio->gc.label = dev_name(&pdev->dev); - cgpio->gc.ngpio = num_gpios; - cgpio->gc.parent = &pdev->dev; - cgpio->gc.base = -1; - cgpio->gc.owner = THIS_MODULE; - cgpio->gc.request = cdns_gpio_request; - cgpio->gc.free = cdns_gpio_free; + cgpio->gen_gc.gc.label = dev_name(&pdev->dev); + cgpio->gen_gc.gc.ngpio = num_gpios; + cgpio->gen_gc.gc.parent = &pdev->dev; + cgpio->gen_gc.gc.base = -1; + cgpio->gen_gc.gc.owner = THIS_MODULE; + cgpio->gen_gc.gc.request = cdns_gpio_request; + cgpio->gen_gc.gc.free = cdns_gpio_free; clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { @@ -210,7 +214,7 @@ static int cdns_gpio_probe(struct platform_device *pdev) if (irq >= 0) { struct gpio_irq_chip *girq; - girq = &cgpio->gc.irq; + girq = &cgpio->gen_gc.gc.irq; gpio_irq_chip_set_chip(girq, &cdns_gpio_irqchip); girq->parent_handler = cdns_gpio_irq_handler; girq->num_parents = 1; @@ -226,7 +230,7 @@ static int cdns_gpio_probe(struct platform_device *pdev) girq->handler = handle_level_irq; } - ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio); + ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gen_gc.gc, cgpio); if (ret < 0) { dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); goto err_revert_dir; -- GitLab From bd9a0dec2d49836e7d186642600e0f668fab33ed Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:14 +0200 Subject: [PATCH 467/868] gpio: 74xx-mmio: use new generic GPIO chip API Convert the driver to using the new generic GPIO chip interfaces from linux/gpio/generic.h. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-7-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-74xx-mmio.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 3ba21add3a1c6..bd2cc5f4f8516 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,8 +19,8 @@ #define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { - struct gpio_chip gc; - unsigned flags; + struct gpio_generic_chip gen_gc; + unsigned int flags; }; static const struct of_device_id mmio_74xx_gpio_ids[] = { @@ -99,16 +100,15 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - if (priv->flags & MMIO_74XX_DIR_OUT) { - gc->set_rv(gc, gpio, val); - return 0; - } + if (priv->flags & MMIO_74XX_DIR_OUT) + return gpio_generic_chip_set(&priv->gen_gc, gpio, val); return -ENOTSUPP; } static int mmio_74xx_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct mmio_74xx_gpio_priv *priv; void __iomem *dat; int err; @@ -123,19 +123,21 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) if (IS_ERR(dat)) return PTR_ERR(dat); - err = bgpio_init(&priv->gc, &pdev->dev, - DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8), - dat, NULL, NULL, NULL, NULL, 0); + config.dev = &pdev->dev; + config.sz = DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8); + config.dat = dat; + + err = gpio_generic_chip_init(&priv->gen_gc, &config); if (err) return err; - priv->gc.direction_input = mmio_74xx_dir_in; - priv->gc.direction_output = mmio_74xx_dir_out; - priv->gc.get_direction = mmio_74xx_get_direction; - priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); - priv->gc.owner = THIS_MODULE; + priv->gen_gc.gc.direction_input = mmio_74xx_dir_in; + priv->gen_gc.gc.direction_output = mmio_74xx_dir_out; + priv->gen_gc.gc.get_direction = mmio_74xx_get_direction; + priv->gen_gc.gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); + priv->gen_gc.gc.owner = THIS_MODULE; - return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); + return devm_gpiochip_add_data(&pdev->dev, &priv->gen_gc.gc, priv); } static struct platform_driver mmio_74xx_gpio_driver = { -- GitLab From 34c029c20300b9b3072f40875b899ef9c40e69cc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:22:15 +0200 Subject: [PATCH 468/868] gpio: en7523: use new generic GPIO chip API Convert the driver to using the new generic GPIO chip interfaces from linux/gpio/generic.h. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250702-gpio-mmio-rework-v2-8-6b77aab684d8@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-en7523.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index c08069d0d1045..cf47afc578a9c 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,28 +14,23 @@ /** * struct airoha_gpio_ctrl - Airoha GPIO driver data - * @gc: Associated gpio_chip instance. + * @gen_gc: Associated gpio_generic_chip instance. * @data: The data register. * @dir: [0] The direction register for the lower 16 pins. * [1]: The direction register for the higher 16 pins. * @output: The output enable register. */ struct airoha_gpio_ctrl { - struct gpio_chip gc; + struct gpio_generic_chip gen_gc; void __iomem *data; void __iomem *dir[2]; void __iomem *output; }; -static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc) -{ - return container_of(gc, struct airoha_gpio_ctrl, gc); -} - static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, int val, int out) { - struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc); u32 dir = ioread32(ctrl->dir[gpio / 16]); u32 output = ioread32(ctrl->output); u32 mask = BIT((gpio % 16) * 2); @@ -50,7 +46,7 @@ static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, iowrite32(dir, ctrl->dir[gpio / 16]); if (out) - gc->set_rv(gc, gpio, val); + gpio_generic_chip_set(&ctrl->gen_gc, gpio, val); iowrite32(output, ctrl->output); @@ -70,7 +66,7 @@ static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio) static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) { - struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + struct airoha_gpio_ctrl *ctrl = gpiochip_get_data(gc); u32 dir = ioread32(ctrl->dir[gpio / 16]); u32 mask = BIT((gpio % 16) * 2); @@ -79,6 +75,7 @@ static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) static int airoha_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config = { }; struct device *dev = &pdev->dev; struct airoha_gpio_ctrl *ctrl; int err; @@ -103,18 +100,21 @@ static int airoha_gpio_probe(struct platform_device *pdev) if (IS_ERR(ctrl->output)) return PTR_ERR(ctrl->output); - err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL, - NULL, NULL, NULL, 0); + config.dev = dev; + config.sz = 4; + config.dat = ctrl->data; + + err = gpio_generic_chip_init(&ctrl->gen_gc, &config); if (err) return dev_err_probe(dev, err, "unable to init generic GPIO"); - ctrl->gc.ngpio = AIROHA_GPIO_MAX; - ctrl->gc.owner = THIS_MODULE; - ctrl->gc.direction_output = airoha_dir_out; - ctrl->gc.direction_input = airoha_dir_in; - ctrl->gc.get_direction = airoha_get_dir; + ctrl->gen_gc.gc.ngpio = AIROHA_GPIO_MAX; + ctrl->gen_gc.gc.owner = THIS_MODULE; + ctrl->gen_gc.gc.direction_output = airoha_dir_out; + ctrl->gen_gc.gc.direction_input = airoha_dir_in; + ctrl->gen_gc.gc.get_direction = airoha_get_dir; - return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); + return devm_gpiochip_add_data(dev, &ctrl->gen_gc.gc, ctrl); } static const struct of_device_id airoha_gpio_of_match[] = { -- GitLab From 47c228d9fc9fe7e3b6f0e7f88f40779f0bb96469 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:02 +0200 Subject: [PATCH 469/868] gpio: tegra186: don't call the set() callback directly Drivers should not dereference GPIO chip callbacks directly. Move the module's set() function higher to make it available to the direction_output() callback and call it instead. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-1-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index d27bfac6c9f53..04effccf9ecdf 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -202,6 +202,26 @@ static int tegra186_init_valid_mask(struct gpio_chip *chip, return 0; } +static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + int level) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + void __iomem *base; + u32 value; + + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return; + + value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); + if (level == 0) + value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + else + value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; + + writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); +} + static int tegra186_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -251,7 +271,7 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, u32 value; /* configure output level first */ - chip->set(chip, offset, level); + tegra186_gpio_set(chip, offset, level); base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) @@ -359,26 +379,6 @@ static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) return value & BIT(0); } -static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, - int level) -{ - struct tegra_gpio *gpio = gpiochip_get_data(chip); - void __iomem *base; - u32 value; - - base = tegra186_gpio_get_base(gpio, offset); - if (WARN_ON(base == NULL)) - return; - - value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); - if (level == 0) - value &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - else - value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; - - writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); -} - static int tegra186_gpio_set_config(struct gpio_chip *chip, unsigned int offset, unsigned long config) -- GitLab From 871e1aee00298fccbda04eacd9e3bb5f46f446b9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:03 +0200 Subject: [PATCH 470/868] gpio: tegra186: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-2-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 04effccf9ecdf..f902da15c4195 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -202,8 +202,8 @@ static int tegra186_init_valid_mask(struct gpio_chip *chip, return 0; } -static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, - int level) +static int tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, + int level) { struct tegra_gpio *gpio = gpiochip_get_data(chip); void __iomem *base; @@ -211,7 +211,7 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) - return; + return -ENODEV; value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); if (level == 0) @@ -220,6 +220,8 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, value |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH; writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); + + return 0; } static int tegra186_gpio_get_direction(struct gpio_chip *chip, @@ -269,9 +271,12 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, struct tegra_gpio *gpio = gpiochip_get_data(chip); void __iomem *base; u32 value; + int ret; /* configure output level first */ - tegra186_gpio_set(chip, offset, level); + ret = tegra186_gpio_set(chip, offset, level); + if (ret) + return ret; base = tegra186_gpio_get_base(gpio, offset); if (WARN_ON(base == NULL)) @@ -886,7 +891,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.direction_input = tegra186_gpio_direction_input; gpio->gpio.direction_output = tegra186_gpio_direction_output; gpio->gpio.get = tegra186_gpio_get; - gpio->gpio.set = tegra186_gpio_set; + gpio->gpio.set_rv = tegra186_gpio_set; gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; gpio->gpio.init_valid_mask = tegra186_init_valid_mask; -- GitLab From 8a81d128e1377a7662d0913bc5013eb8a90c3e33 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:04 +0200 Subject: [PATCH 471/868] gpio: tegra: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-3-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 9ad286adf2632..126fd12550aa8 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -146,12 +146,14 @@ static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) tegra_gpio_disable(tgi, offset); } -static void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); + + return 0; } static int tegra_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -718,7 +720,7 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->gc.direction_input = tegra_gpio_direction_input; tgi->gc.get = tegra_gpio_get; tgi->gc.direction_output = tegra_gpio_direction_output; - tgi->gc.set = tegra_gpio_set; + tgi->gc.set_rv = tegra_gpio_set; tgi->gc.get_direction = tegra_gpio_get_direction; tgi->gc.base = 0; tgi->gc.ngpio = tgi->bank_count * 32; -- GitLab From ecf0c0278f4799f6af245131f02dbb8587f87d29 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:05 +0200 Subject: [PATCH 472/868] gpio: thunderx: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-4-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-thunderx.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 5b851e904c11f..eb6a1f0279c09 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -116,8 +116,8 @@ static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line) return 0; } -static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, - int value) +static int thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, + int value) { struct thunderx_gpio *txgpio = gpiochip_get_data(chip); int bank = line / 64; @@ -127,6 +127,8 @@ static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, (bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR); writeq(BIT_ULL(bank_bit), reg); + + return 0; } static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, @@ -269,9 +271,9 @@ static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line) return masked_bits != 0; } -static void thunderx_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, - unsigned long *bits) +static int thunderx_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) { int bank; u64 set_bits, clear_bits; @@ -283,6 +285,8 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET); writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR); } + + return 0; } static void thunderx_gpio_irq_ack(struct irq_data *d) @@ -529,8 +533,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, chip->direction_input = thunderx_gpio_dir_in; chip->get = thunderx_gpio_get; chip->direction_output = thunderx_gpio_dir_out; - chip->set = thunderx_gpio_set; - chip->set_multiple = thunderx_gpio_set_multiple; + chip->set_rv = thunderx_gpio_set; + chip->set_multiple_rv = thunderx_gpio_set_multiple; chip->set_config = thunderx_gpio_set_config; girq = &chip->irq; gpio_irq_chip_set_chip(girq, &thunderx_gpio_irq_chip); -- GitLab From dd66f8862f8418d989a8e342ff7af26bf4a0ae8f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:06 +0200 Subject: [PATCH 473/868] gpio: timberdale: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-5-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-timberdale.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index cb303a26f4d3c..fbb8830891890 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -80,10 +80,9 @@ static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, return timbgpio_update_bit(gpio, nr, TGPIODIR, false); } -static void timbgpio_gpio_set(struct gpio_chip *gpio, - unsigned nr, int val) +static int timbgpio_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { - timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); + return timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); } static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) @@ -254,7 +253,7 @@ static int timbgpio_probe(struct platform_device *pdev) gc->direction_input = timbgpio_gpio_direction_input; gc->get = timbgpio_gpio_get; gc->direction_output = timbgpio_gpio_direction_output; - gc->set = timbgpio_gpio_set; + gc->set_rv = timbgpio_gpio_set; gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; gc->dbg_show = NULL; gc->base = pdata->gpio_base; -- GitLab From 00c337cc68c34302fc0af2d9ac47be5c638d4a78 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:07 +0200 Subject: [PATCH 474/868] gpio: tpic2810: remove unneeded callbacks GPIO core can handle output-only chips that don't implement the get() and direction_input() callbacks. There's no need to provide dummy implementation of the latter in the driver so drop it. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-6-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tpic2810.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index effb7b8ff81fd..e99725bd3d63d 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -34,13 +34,6 @@ static int tpic2810_get_direction(struct gpio_chip *chip, return GPIO_LINE_DIRECTION_OUT; } -static int tpic2810_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - /* This device is output only */ - return -EINVAL; -} - static int tpic2810_direction_output(struct gpio_chip *chip, unsigned offset, int value) { @@ -83,7 +76,6 @@ static const struct gpio_chip template_chip = { .label = "tpic2810", .owner = THIS_MODULE, .get_direction = tpic2810_get_direction, - .direction_input = tpic2810_direction_input, .direction_output = tpic2810_direction_output, .set = tpic2810_set, .set_multiple = tpic2810_set_multiple, -- GitLab From 4ffdd9d8a37eaf4e25855e1038b9e1091401c252 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:08 +0200 Subject: [PATCH 475/868] gpio: tpic2810: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-7-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tpic2810.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index e99725bd3d63d..d5b8568ab0613 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -25,7 +25,7 @@ struct tpic2810 { struct mutex lock; }; -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value); +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value); static int tpic2810_get_direction(struct gpio_chip *chip, unsigned offset) @@ -38,8 +38,7 @@ static int tpic2810_direction_output(struct gpio_chip *chip, unsigned offset, int value) { /* This device always output */ - tpic2810_set(chip, offset, value); - return 0; + return tpic2810_set(chip, offset, value); } static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) @@ -61,15 +60,19 @@ static void tpic2810_set_mask_bits(struct gpio_chip *chip, u8 mask, u8 bits) mutex_unlock(&gpio->lock); } -static void tpic2810_set(struct gpio_chip *chip, unsigned offset, int value) +static int tpic2810_set(struct gpio_chip *chip, unsigned int offset, int value) { tpic2810_set_mask_bits(chip, BIT(offset), value ? BIT(offset) : 0); + + return 0; } -static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, - unsigned long *bits) +static int tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) { tpic2810_set_mask_bits(chip, *mask, *bits); + + return 0; } static const struct gpio_chip template_chip = { @@ -77,8 +80,8 @@ static const struct gpio_chip template_chip = { .owner = THIS_MODULE, .get_direction = tpic2810_get_direction, .direction_output = tpic2810_direction_output, - .set = tpic2810_set, - .set_multiple = tpic2810_set_multiple, + .set_rv = tpic2810_set, + .set_multiple_rv = tpic2810_set_multiple, .base = -1, .ngpio = 8, .can_sleep = true, -- GitLab From 2a5be7a80b3b1036fba5dae13dd55c97fb7eabaa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:09 +0200 Subject: [PATCH 476/868] gpio: tps65086: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-8-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65086.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 8f5827554e1e8..08fa061b73efc 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -37,10 +37,8 @@ static int tps65086_gpio_direction_output(struct gpio_chip *chip, struct tps65086_gpio *gpio = gpiochip_get_data(chip); /* Set the initial value */ - regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, - BIT(4 + offset), value ? BIT(4 + offset) : 0); - - return 0; + return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); } static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -55,13 +53,13 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset) return val & BIT(4 + offset); } -static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) +static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tps65086_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, - BIT(4 + offset), value ? BIT(4 + offset) : 0); + return regmap_update_bits(gpio->tps->regmap, TPS65086_GPOCTRL, + BIT(4 + offset), value ? BIT(4 + offset) : 0); } static const struct gpio_chip template_chip = { @@ -71,7 +69,7 @@ static const struct gpio_chip template_chip = { .direction_input = tps65086_gpio_direction_input, .direction_output = tps65086_gpio_direction_output, .get = tps65086_gpio_get, - .set = tps65086_gpio_set, + .set_rv = tps65086_gpio_set, .base = -1, .ngpio = 4, .can_sleep = true, -- GitLab From 913cbf8a0d4b0554d66cdc608b231cdf2401a496 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:10 +0200 Subject: [PATCH 477/868] gpio: tps65218: remove unneeded callbacks GPIO core can handle output-only chips that don't implement the get() and direction_input() callbacks. There's no need to provide dummy implementation of the latter in the driver so drop it. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-9-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65218.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index d7d9d50dcddf4..68e4f0a19f4eb 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -59,11 +59,6 @@ static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, return 0; } -static int tps65218_gpio_input(struct gpio_chip *gc, unsigned offset) -{ - return -EPERM; -} - static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) { struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); @@ -174,7 +169,6 @@ static const struct gpio_chip template_chip = { .owner = THIS_MODULE, .request = tps65218_gpio_request, .direction_output = tps65218_gpio_output, - .direction_input = tps65218_gpio_input, .get = tps65218_gpio_get, .set = tps65218_gpio_set, .set_config = tps65218_gpio_set_config, -- GitLab From 4ca81a1f3a46603986cbdd11348433b0746ce483 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:11 +0200 Subject: [PATCH 478/868] gpio: tps65218: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-10-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65218.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 68e4f0a19f4eb..49cd7754ed053 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -34,29 +34,28 @@ static int tps65218_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & (TPS65218_ENABLE2_GPIO1 << offset)); } -static void tps65218_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65218_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65218_gpio *tps65218_gpio = gpiochip_get_data(gc); struct tps65218 *tps65218 = tps65218_gpio->tps65218; if (value) - tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_PROTECT_L1); - else - tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, - TPS65218_ENABLE2_GPIO1 << offset, - TPS65218_PROTECT_L1); + return tps65218_set_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); + + return tps65218_clear_bits(tps65218, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_GPIO1 << offset, + TPS65218_PROTECT_L1); } static int tps65218_gpio_output(struct gpio_chip *gc, unsigned offset, int value) { /* Only drives GPOs */ - tps65218_gpio_set(gc, offset, value); - return 0; + return tps65218_gpio_set(gc, offset, value); } static int tps65218_gpio_request(struct gpio_chip *gc, unsigned offset) @@ -170,7 +169,7 @@ static const struct gpio_chip template_chip = { .request = tps65218_gpio_request, .direction_output = tps65218_gpio_output, .get = tps65218_gpio_get, - .set = tps65218_gpio_set, + .set_rv = tps65218_gpio_set, .set_config = tps65218_gpio_set_config, .can_sleep = true, .ngpio = 3, -- GitLab From fc0e4091afa9b6c02f1cc2ddec75b248c25cefe6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:12 +0200 Subject: [PATCH 479/868] gpio: tps65219: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-11-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65219.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 526640c39a11f..630cb41e77a4e 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -65,10 +65,9 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) return ret; } -static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); - struct device *dev = gpio->tps->dev; int v, mask, bit; bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; @@ -76,8 +75,8 @@ static void tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int val mask = BIT(bit); v = value ? mask : 0; - if (regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, mask, v)) - dev_err(dev, "GPIO%d, set to value %d failed.\n", offset, value); + return regmap_update_bits(gpio->tps->regmap, + TPS65219_REG_GENERAL_CONFIG, mask, v); } static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, @@ -147,7 +146,7 @@ static const struct gpio_chip tps65219_template_chip = { .direction_input = tps65219_gpio_direction_input, .direction_output = tps65219_gpio_direction_output, .get = tps65219_gpio_get, - .set = tps65219_gpio_set, + .set_rv = tps65219_gpio_set, .base = -1, .ngpio = 3, .can_sleep = true, -- GitLab From e3ec7ad5ab139ad9e0ed8931ba562f95698c3b2f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 2 Jul 2025 11:14:13 +0200 Subject: [PATCH 480/868] gpio: tps6586x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250702-gpiochip-set-rv-gpio-round3-v1-12-0d23be74f71d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps6586x.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c index d277aa951143c..f1ced092f38a5 100644 --- a/drivers/gpio/gpio-tps6586x.c +++ b/drivers/gpio/gpio-tps6586x.c @@ -40,13 +40,13 @@ static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset) return !!(val & (1 << offset)); } -static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps6586x_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc); - tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, - value << offset, 1 << offset); + return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2, + value << offset, 1 << offset); } static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -54,8 +54,11 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, { struct tps6586x_gpio *tps6586x_gpio = gpiochip_get_data(gc); uint8_t val, mask; + int ret; - tps6586x_gpio_set(gc, offset, value); + ret = tps6586x_gpio_set(gc, offset, value); + if (ret) + return ret; val = 0x1 << (offset * 2); mask = 0x3 << (offset * 2); @@ -95,7 +98,7 @@ static int tps6586x_gpio_probe(struct platform_device *pdev) /* FIXME: add handling of GPIOs as dedicated inputs */ tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output; - tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set; + tps6586x_gpio->gpio_chip.set_rv = tps6586x_gpio_set; tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get; tps6586x_gpio->gpio_chip.to_irq = tps6586x_gpio_to_irq; -- GitLab From 3263a554f396422b07ab42831c2f106f9596b9f4 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 6 Jul 2025 18:09:03 +0200 Subject: [PATCH 481/868] ALSA: mts64: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250706160906.416270-1-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/drivers/mts64.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 17f215bad0ec7..bbeebbe467eac 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -763,7 +764,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card) return err; rmidi->private_data = mts; - strcpy(rmidi->name, CARD_NAME); + strscpy(rmidi->name, CARD_NAME); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -791,7 +792,7 @@ static int snd_mts64_rawmidi_create(struct snd_card *card) mts->midi_input_substream[substream->number] = substream; switch(substream->number) { case MTS64_SMPTE_SUBSTREAM: - strcpy(substream->name, "Miditerminal SMPTE"); + strscpy(substream->name, "Miditerminal SMPTE"); break; default: sprintf(substream->name, @@ -929,8 +930,8 @@ static int snd_mts64_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Cannot create card\n"); return err; } - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "ESI " CARD_NAME); + strscpy(card->driver, DRIVER_NAME); + strscpy(card->shortname, "ESI " CARD_NAME); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); -- GitLab From c5f0cd2bd6ae8376b01232766ffd8d30042dfaa9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:08 +0300 Subject: [PATCH 482/868] gpio: arizona: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Reviewed-by: Charles Keepax Signed-off-by: Sakari Ailus Link: https://lore.kernel.org/r/20250704075408.3217690-1-sakari.ailus@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-arizona.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index e530c94dcce8d..89ffde6930190 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -39,7 +39,6 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset) return ret; if (change && persistent) { - pm_runtime_mark_last_busy(chip->parent); pm_runtime_put_autosuspend(chip->parent); } @@ -82,7 +81,6 @@ static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) return ret; } - pm_runtime_mark_last_busy(chip->parent); pm_runtime_put_autosuspend(chip->parent); } -- GitLab From 453de04bf722bc939bfa2f1341eb6b3389605912 Mon Sep 17 00:00:00 2001 From: Maria Garcia Date: Thu, 3 Jul 2025 22:57:39 +0200 Subject: [PATCH 483/868] dt-bindings: gpio: pca95xx: add TI TCA6418 The TCA6418E is a 18-channel I2C I/O expander with integrated ESD protection. Signed-off-by: Maria Garcia Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250703205740.45385-2-mariagarcia7293@gmail.com Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 4d3f52f8d1b82..12134c737ad8f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -68,6 +68,7 @@ properties: - ti,pca9536 - ti,tca6408 - ti,tca6416 + - ti,tca6418 - ti,tca6424 - ti,tca9535 - ti,tca9538 -- GitLab From 6c99a046edfac782b5ec3a3a1a5f0633bed28563 Mon Sep 17 00:00:00 2001 From: Maria Garcia Date: Thu, 3 Jul 2025 22:57:40 +0200 Subject: [PATCH 484/868] gpio: pca953x: Add support for TI TCA6418 The TI TCA6418 is a 18-channel I2C I/O expander. It is slightly different to other models from the same family, such as TCA6416, but has enough in common with them to make it work with just a few tweaks, which are explained in the code's documentation. Signed-off-by: Maria Garcia Link: https://lore.kernel.org/r/20250703205740.45385-3-mariagarcia7293@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 153 +++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b852e49976294..f5184860bd89f 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -38,6 +38,10 @@ #define PCA953X_INVERT 0x02 #define PCA953X_DIRECTION 0x03 +#define TCA6418_INPUT 0x14 +#define TCA6418_OUTPUT 0x17 +#define TCA6418_DIRECTION 0x23 + #define REG_ADDR_MASK GENMASK(5, 0) #define REG_ADDR_EXT BIT(6) #define REG_ADDR_AI BIT(7) @@ -76,7 +80,8 @@ #define PCA953X_TYPE BIT(12) #define PCA957X_TYPE BIT(13) #define PCAL653X_TYPE BIT(14) -#define PCA_TYPE_MASK GENMASK(15, 12) +#define TCA6418_TYPE BIT(16) +#define PCA_TYPE_MASK GENMASK(16, 12) #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) @@ -115,6 +120,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca6107", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, + { "tca6418", 18 | TCA6418_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { "tca9538", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, @@ -204,6 +210,13 @@ static const struct pca953x_reg_config pca957x_regs = { .invert = PCA957X_INVRT, }; +static const struct pca953x_reg_config tca6418_regs = { + .direction = TCA6418_DIRECTION, + .output = TCA6418_OUTPUT, + .input = TCA6418_INPUT, + .invert = 0xFF, /* Does not apply */ +}; + struct pca953x_chip { unsigned gpio_start; struct mutex i2c_lock; @@ -237,6 +250,22 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); } +/* + * Helper function to get the correct bit mask for a given offset and chip type. + * The TCA6418's input, output, and direction banks have a peculiar bit order: + * the first byte uses reversed bit order, while the second byte uses standard order. + */ +static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int offset) +{ + unsigned int bit_pos_in_bank = offset % BANK_SZ; + int msb = BANK_SZ - 1; + + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE && offset <= msb) + return BIT(msb - bit_pos_in_bank); + + return BIT(bit_pos_in_bank); +} + #define PCA953x_BANK_INPUT BIT(0) #define PCA953x_BANK_OUTPUT BIT(1) #define PCA953x_BANK_POLARITY BIT(2) @@ -353,18 +382,43 @@ static bool pcal6534_check_register(struct pca953x_chip *chip, unsigned int reg, return true; } +/* TCA6418 breaks the PCA953x register order rule */ +static bool tca6418_check_register(struct pca953x_chip *chip, unsigned int reg, + u32 access_type_mask) +{ + /* Valid Input Registers - BIT(0) for readable access */ + if (reg >= TCA6418_INPUT && reg < (TCA6418_INPUT + NBANK(chip))) + return (access_type_mask & BIT(0)); + + /* Valid Output Registers - BIT(1) for writeable access */ + if (reg >= TCA6418_OUTPUT && reg < (TCA6418_OUTPUT + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + /* Valid Direction Registers - BIT(2) for volatile access */ + if (reg >= TCA6418_DIRECTION && reg < (TCA6418_DIRECTION + NBANK(chip))) + return (access_type_mask & (BIT(0) | BIT(1))); + + return false; +} + static bool pca953x_readable_register(struct device *dev, unsigned int reg) { struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(0) to indicate read access */ + return tca6418_check_register(chip, reg, BIT(0)); + default: bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) { @@ -381,12 +435,18 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD; - } else { + break; + case TCA6418_TYPE: + /* BIT(1) for write access */ + return tca6418_check_register(chip, reg, BIT(1)); + default: bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG; + break; } if (chip->driver_data & PCA_PCAL) @@ -401,10 +461,17 @@ static bool pca953x_volatile_register(struct device *dev, unsigned int reg) struct pca953x_chip *chip = dev_get_drvdata(dev); u32 bank; - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: bank = PCA957x_BANK_INPUT; - else + break; + case TCA6418_TYPE: + /* BIT(2) for volatile access */ + return tca6418_check_register(chip, reg, BIT(2)); + default: bank = PCA953x_BANK_INPUT; + break; + } if (chip->driver_data & PCA_PCAL) bank |= PCAL9xxx_BANK_IRQ_STAT; @@ -489,6 +556,16 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off) return pinctrl + addr + (off / BANK_SZ); } +static u8 tca6418_recalc_addr(struct pca953x_chip *chip, int reg_base, int offset) +{ + /* + * reg_base will be TCA6418_INPUT, TCA6418_OUTPUT, or TCA6418_DIRECTION + * offset is the global GPIO line offset (0-17) + * BANK_SZ is 8 for TCA6418 (8 bits per register bank) + */ + return reg_base + (offset / BANK_SZ); +} + static int pca953x_write_regs(struct pca953x_chip *chip, int reg, unsigned long *val) { u8 regaddr = chip->recalc_addr(chip, reg, 0); @@ -529,10 +606,13 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_write_bits(chip->regmap, dirreg, bit, 0); + return regmap_write_bits(chip->regmap, dirreg, bit, bit); } @@ -542,7 +622,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); int ret; guard(mutex)(&chip->i2c_lock); @@ -552,7 +632,13 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, if (ret) return ret; - /* then direction */ + /* + * then direction + * (in/out logic is inverted on TCA6418) + */ + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return regmap_write_bits(chip->regmap, dirreg, bit, bit); + return regmap_write_bits(chip->regmap, dirreg, bit, 0); } @@ -560,7 +646,7 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 inreg = chip->recalc_addr(chip, chip->regs->input, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -577,7 +663,7 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 outreg = chip->recalc_addr(chip, chip->regs->output, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); guard(mutex)(&chip->i2c_lock); @@ -588,7 +674,7 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 dirreg = chip->recalc_addr(chip, chip->regs->direction, off); - u8 bit = BIT(off % BANK_SZ); + u8 bit = pca953x_get_bit_mask(chip, off); u32 reg_val; int ret; @@ -597,7 +683,14 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) if (ret < 0) return ret; - if (reg_val & bit) + /* (in/out logic is inverted on TCA6418) */ + if (reg_val & bit) { + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; + } + if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -1117,12 +1210,22 @@ static int pca953x_probe(struct i2c_client *client) regmap_config = &pca953x_i2c_regmap; } - if (PCA_CHIP_TYPE(chip->driver_data) == PCAL653X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCAL653X_TYPE: chip->recalc_addr = pcal6534_recalc_addr; chip->check_reg = pcal6534_check_register; - } else { + break; + case TCA6418_TYPE: + chip->recalc_addr = tca6418_recalc_addr; + /* + * We don't assign chip->check_reg = tca6418_check_register directly here. + * Instead, the wrappers handle the dispatch based on PCA_CHIP_TYPE. + */ + break; + default: chip->recalc_addr = pca953x_recalc_addr; chip->check_reg = pca953x_check_register; + break; } chip->regmap = devm_regmap_init_i2c(client, regmap_config); @@ -1151,15 +1254,22 @@ static int pca953x_probe(struct i2c_client *client) lockdep_set_subclass(&chip->i2c_lock, i2c_adapter_depth(client->adapter)); - /* initialize cached registers from their original values. + /* + * initialize cached registers from their original values. * we can't share this chip with another i2c master. */ - if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) { + switch (PCA_CHIP_TYPE(chip->driver_data)) { + case PCA957X_TYPE: chip->regs = &pca957x_regs; ret = device_pca957x_init(chip); - } else { + break; + case TCA6418_TYPE: + chip->regs = &tca6418_regs; + break; + default: chip->regs = &pca953x_regs; ret = device_pca95xx_init(chip); + break; } if (ret) return ret; @@ -1325,6 +1435,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, + { .compatible = "ti,tca6418", .data = (void *)(18 | TCA6418_TYPE | PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), }, @@ -1355,7 +1466,9 @@ static int __init pca953x_init(void) { return i2c_add_driver(&pca953x_driver); } -/* register after i2c postcore initcall and before + +/* + * register after i2c postcore initcall and before * subsys initcalls that may rely on these GPIOs */ subsys_initcall(pca953x_init); -- GitLab From 56036d6af41a473a8129fc960a5ab3673eda13d5 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Mon, 9 Jun 2025 13:46:57 -0500 Subject: [PATCH 485/868] platform/x86: dell_rbu: Remove unused struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop using an entire struct packet_data just for its embedded list_head. Signed-off-by: Stuart Hayes Link: https://lore.kernel.org/r/20250609184659.7210-4-stuart.w.hayes@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell_rbu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 9dd9f2cb074f8..45c0a72e494ab 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -77,14 +77,14 @@ struct packet_data { int ordernum; }; -static struct packet_data packet_data_head; +static struct list_head packet_data_list; static struct platform_device *rbu_device; static int context; static void init_packet_head(void) { - INIT_LIST_HEAD(&packet_data_head.list); + INIT_LIST_HEAD(&packet_data_list); rbu_data.packet_read_count = 0; rbu_data.num_packets = 0; rbu_data.packetsize = 0; @@ -183,7 +183,7 @@ static int create_packet(void *data, size_t length) __must_hold(&rbu_data.lock) /* initialize the newly created packet headers */ INIT_LIST_HEAD(&newpacket->list); - list_add_tail(&newpacket->list, &packet_data_head.list); + list_add_tail(&newpacket->list, &packet_data_list); memcpy(newpacket->data, data, length); @@ -292,7 +292,7 @@ static int packet_read_list(char *data, size_t * pread_length) remaining_bytes = *pread_length; bytes_read = rbu_data.packet_read_count; - list_for_each_entry(newpacket, &packet_data_head.list, list) { + list_for_each_entry(newpacket, &packet_data_list, list) { bytes_copied = do_packet_read(pdest, newpacket, remaining_bytes, bytes_read, &temp_count); remaining_bytes -= bytes_copied; @@ -315,7 +315,7 @@ static void packet_empty_list(void) { struct packet_data *newpacket, *tmp; - list_for_each_entry_safe(newpacket, tmp, &packet_data_head.list, list) { + list_for_each_entry_safe(newpacket, tmp, &packet_data_list, list) { list_del(&newpacket->list); /* -- GitLab From e199e85556bd83806baf4689cbecfa31c41a6cfa Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:20 +0000 Subject: [PATCH 486/868] ASoC: soc-dapm: remove unnecessary definition Below functions are already defined in soc-dapm.h, it is not necessary in soc-dapm.c. Remove these snd_soc_dapm_new_control() snd_soc_dapm_new_control_unlocked() Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87wm8shitv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f26f9e9d7ce74..091ab31ae42dd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -55,14 +55,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink)); -struct snd_soc_dapm_widget * -snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); - -struct snd_soc_dapm_widget * -snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); - static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg); /* dapm power sequences - make this per codec in the future */ -- GitLab From 58baaea26659ab24ba396afd592c2423aefc067c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:27 +0000 Subject: [PATCH 487/868] ASoC: soc-dapm: remove EXPORT_SYMBOL_GPL() for snd_soc_dapm_free() snd_soc_dapm_free() is called from soc-dapm.c / soc-core.c only. All these are compiled by snd-soc-core-y. So EXPORT_SYMBOL_GPL() is not needed. Remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87v7ochitp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 091ab31ae42dd..98b35600904d4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4883,7 +4883,6 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) dapm_free_widgets(dapm); list_del(&dapm->list); } -EXPORT_SYMBOL_GPL(snd_soc_dapm_free); void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card, -- GitLab From 2ec1067d1e5acb9e7aeb1fe6d5178424fc3107ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:33 +0000 Subject: [PATCH 488/868] ASoC: soc-dapm: remove snd_soc_dapm_nc_pin[_unlocked]() snd_soc_dapm_nc_pin() was added in commit 5817b52a298a ("ALSA: ASoC: Allow machine drivers to mark pins as not connected") at 2008. It is identical to snd_soc_dapm_disable_pin[_unlocked](). It was expected to be updated, but were enough as-is for this 17 years. We might update these, but renaming function name by define is enough for now. We can re-create these if needed in the future. Let's remove it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87tt3whitj.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 12 ++++++++-- sound/soc/soc-dapm.c | 51 ---------------------------------------- 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 400584474bc8b..2a0250fff9ba6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -498,8 +498,6 @@ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); @@ -509,6 +507,16 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *p unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); void dapm_mark_endpoints_dirty(struct snd_soc_card *card); +/* + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + */ +#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin +#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked + /* dapm path query */ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 98b35600904d4..e4c2feec6e39f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4772,57 +4772,6 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); -/** - * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. - * @dapm: DAPM context - * @pin: pin name - * - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - * - * Requires external locking. - * - * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to - * do any widget power switching. - */ -int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, - const char *pin) -{ - return snd_soc_dapm_set_pin(dapm, pin, 0); -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); - -/** - * snd_soc_dapm_nc_pin - permanently disable pin. - * @dapm: DAPM context - * @pin: pin name - * - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - * - * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to - * do any widget power switching. - */ -int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) -{ - int ret; - - snd_soc_dapm_mutex_lock(dapm); - - ret = snd_soc_dapm_set_pin(dapm, pin, 0); - - snd_soc_dapm_mutex_unlock(dapm); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); - /** * snd_soc_dapm_get_pin_status - get audio pin status * @dapm: DAPM context -- GitLab From d6f240031afbd780431ef289357373e2bbf2f793 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:39 +0000 Subject: [PATCH 489/868] ASoC: soc-dapm: remove snd_soc_dapm_weak_routes() No one is using snd_soc_dapm_weak_routes(), let's remove it. Because snd_soc_dapm_weak_routes() was removed, path->weak is not needed either. Remove it, too. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87sejghitd.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 3 -- sound/soc/soc-dapm.c | 91 ++-------------------------------------- 2 files changed, 4 insertions(+), 90 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2a0250fff9ba6..6d854c727bca8 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -473,8 +473,6 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); -int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route, int num); void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); /* dapm events */ @@ -611,7 +609,6 @@ struct snd_soc_dapm_path { /* status */ u32 connect:1; /* source and sink widgets are connected */ u32 walking:1; /* path is in the process of being walked */ - u32 weak:1; /* path ignored for power management */ u32 is_supply:1; /* At least one of the connected widgets is a supply */ int (*connected)(struct snd_soc_dapm_widget *source, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e4c2feec6e39f..79cd3c93a4fd3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -214,7 +214,7 @@ static __always_inline void dapm_widget_invalidate_paths( list_for_each_entry(w, &list, work_list) { snd_soc_dapm_widget_for_each_path(w, dir, p) { - if (p->is_supply || p->weak || !p->connect) + if (p->is_supply || !p->connect) continue; node = p->node[rdir]; if (node->endpoints[dir] != -1) { @@ -276,7 +276,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) * Weak paths or supply paths do not influence the number of input or * output paths of their neighbors. */ - if (p->weak || p->is_supply) + if (p->is_supply) return; /* @@ -1162,7 +1162,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget, widget->endpoints[dir] = -1; snd_soc_dapm_widget_for_each_path(widget, rdir, path) { - if (path->weak || path->is_supply) + if (path->is_supply) continue; if (path->walking) @@ -1217,7 +1217,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, snd_soc_dapm_widget_for_each_path(widget, rdir, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); - if (path->weak || path->is_supply) + if (path->is_supply) continue; if (path->walking) @@ -1454,9 +1454,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) snd_soc_dapm_widget_for_each_sink_path(w, path) { DAPM_UPDATE_STAT(w, neighbour_checks); - if (path->weak) - continue; - if (path->connected && !path->connected(path->source, path->sink)) continue; @@ -3202,86 +3199,6 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); -static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) -{ - struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, - route->source, - true); - struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, - route->sink, - true); - struct snd_soc_dapm_path *path; - int count = 0; - - if (!source) { - dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n", - route->source); - return -ENODEV; - } - - if (!sink) { - dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n", - route->sink); - return -ENODEV; - } - - if (route->control || route->connected) - dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", - route->source, route->sink); - - snd_soc_dapm_widget_for_each_sink_path(source, path) { - if (path->sink == sink) { - path->weak = 1; - count++; - } - } - - if (count == 0) - dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n", - route->source, route->sink); - if (count > 1) - dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n", - count, route->source, route->sink); - - return 0; -} - -/** - * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak - * @dapm: DAPM context - * @route: audio routes - * @num: number of routes - * - * Mark existing routes matching those specified in the passed array - * as being weak, meaning that they are ignored for the purpose of - * power decisions. The main intended use case is for sidetone paths - * which couple audio between other independent paths if they are both - * active in order to make the combination work better at the user - * level but which aren't intended to be "used". - * - * Note that CODEC drivers should not use this as sidetone type paths - * can frequently also be used as bypass paths. - */ -int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route, int num) -{ - int i; - int ret = 0; - - snd_soc_dapm_mutex_lock_root(dapm); - for (i = 0; i < num; i++) { - int err = snd_soc_dapm_weak_route(dapm, route); - if (err) - ret = err; - route++; - } - snd_soc_dapm_mutex_unlock(dapm); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); - /** * snd_soc_dapm_new_widgets - add new dapm widgets * @card: card to be checked for new dapm widgets -- GitLab From 0d516af948536c3fdefadd465a516e1f1317f696 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:45 +0000 Subject: [PATCH 490/868] ASoC: soc-dapm: reordering function definitions Because functions are defined randomly, it needs function name definitions on top of soc-dapm.c. it is not needed if functions are implemented in correct order. This patch has big change from change-line point of view, but is just reordering, nothing changed in meaning. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzz0hit6.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 733 +++++++++++++++++++++---------------------- 1 file changed, 364 insertions(+), 369 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 79cd3c93a4fd3..babb642c96558 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -49,14 +49,6 @@ for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ (dir)++) -static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, - const char *control, - int (*connected)(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink)); - -static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg); - /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 1, @@ -346,6 +338,320 @@ struct dapm_kcontrol_data { struct snd_soc_dapm_widget_list *wlist; }; +static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) +{ + if (!dapm->component) + return -EIO; + return snd_soc_component_read(dapm->component, reg); +} + +/* set up initial codec paths */ +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, + int nth_path) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *) + p->sink->kcontrol_news[i].private_value; + unsigned int reg = mc->reg; + unsigned int invert = mc->invert; + + if (reg != SND_SOC_NOPM) { + unsigned int shift = mc->shift; + unsigned int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int val = soc_dapm_read(p->sink->dapm, reg); + + /* + * The nth_path argument allows this function to know + * which path of a kcontrol it is setting the initial + * status for. Ideally this would support any number + * of paths and channels. But since kcontrols only come + * in mono and stereo variants, we are limited to 2 + * channels. + * + * The following code assumes for stereo controls the + * first path is the left channel, and all remaining + * paths are the right channel. + */ + if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { + if (reg != mc->rreg) + val = soc_dapm_read(p->sink->dapm, mc->rreg); + val = (val >> mc->rshift) & mask; + } else { + val = (val >> shift) & mask; + } + if (invert) + val = max - val; + p->connect = !!val; + } else { + /* since a virtual mixer has no backing registers to + * decide which path to connect, it will try to match + * with initial state. This is to ensure + * that the default mixer choice will be + * correctly powered up during initialization. + */ + p->connect = invert; + } +} + +/* connect mux widget to its interconnecting audio paths */ +static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_path *path, const char *control_name, + struct snd_soc_dapm_widget *w) +{ + const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int item; + int i; + + if (e->reg != SND_SOC_NOPM) { + unsigned int val; + + val = soc_dapm_read(dapm, e->reg); + val = (val >> e->shift_l) & e->mask; + item = snd_soc_enum_val_to_item(e, val); + } else { + /* since a virtual mux has no backing registers to + * decide which path to connect, it will try to match + * with the first enumeration. This is to ensure + * that the default mux choice (the first) will be + * correctly powered up during initialization. + */ + item = 0; + } + + i = match_string(e->texts, e->items, control_name); + if (i < 0) + return -ENODEV; + + path->name = e->texts[i]; + path->connect = (i == item); + return 0; + +} + +/* connect mixer widget to its interconnecting audio paths */ +static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_path *path, const char *control_name) +{ + int i, nth_path = 0; + + /* search for mixer kcontrol */ + for (i = 0; i < path->sink->num_kcontrols; i++) { + if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { + path->name = path->sink->kcontrol_news[i].name; + dapm_set_mixer_path_status(path, i, nth_path++); + return 0; + } + } + return -ENODEV; +} + +/* + * dapm_update_widget_flags() - Re-compute widget sink and source flags + * @w: The widget for which to update the flags + * + * Some widgets have a dynamic category which depends on which neighbors they + * are connected to. This function update the category for these widgets. + * + * This function must be called whenever a path is added or removed to a widget. + */ +static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) +{ + enum snd_soc_dapm_direction dir; + struct snd_soc_dapm_path *p; + unsigned int ep; + + switch (w->id) { + case snd_soc_dapm_input: + /* On a fully routed card an input is never a source */ + if (w->dapm->card->fully_routed) + return; + ep = SND_SOC_DAPM_EP_SOURCE; + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (p->source->id == snd_soc_dapm_micbias || + p->source->id == snd_soc_dapm_mic || + p->source->id == snd_soc_dapm_line || + p->source->id == snd_soc_dapm_output) { + ep = 0; + break; + } + } + break; + case snd_soc_dapm_output: + /* On a fully routed card a output is never a sink */ + if (w->dapm->card->fully_routed) + return; + ep = SND_SOC_DAPM_EP_SINK; + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (p->sink->id == snd_soc_dapm_spk || + p->sink->id == snd_soc_dapm_hp || + p->sink->id == snd_soc_dapm_line || + p->sink->id == snd_soc_dapm_input) { + ep = 0; + break; + } + } + break; + case snd_soc_dapm_line: + ep = 0; + snd_soc_dapm_for_each_direction(dir) { + if (!list_empty(&w->edges[dir])) + ep |= SND_SOC_DAPM_DIR_TO_EP(dir); + } + break; + default: + return; + } + + w->is_ep = ep; +} + +static int snd_soc_dapm_check_dynamic_path( + struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, + const char *control) +{ + bool dynamic_source = false; + bool dynamic_sink = false; + + if (!control) + return 0; + + switch (source->id) { + case snd_soc_dapm_demux: + dynamic_source = true; + break; + default: + break; + } + + switch (sink->id) { + case snd_soc_dapm_mux: + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + dynamic_sink = true; + break; + default: + break; + } + + if (dynamic_source && dynamic_sink) { + dev_err(dapm->dev, + "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } else if (!dynamic_source && !dynamic_sink) { + dev_err(dapm->dev, + "Control not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } + + return 0; +} + +static int snd_soc_dapm_add_path( + struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)) +{ + enum snd_soc_dapm_direction dir; + struct snd_soc_dapm_path *path; + int ret; + + if (wsink->is_supply && !wsource->is_supply) { + dev_err(dapm->dev, + "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (connected && !wsource->is_supply) { + dev_err(dapm->dev, + "connected() callback only supported for supply widgets (%s -> %s)\n", + wsource->name, wsink->name); + return -EINVAL; + } + + if (wsource->is_supply && control) { + dev_err(dapm->dev, + "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", + wsource->name, control, wsink->name); + return -EINVAL; + } + + ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); + if (ret) + return ret; + + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); + if (!path) + return -ENOMEM; + + path->node[SND_SOC_DAPM_DIR_IN] = wsource; + path->node[SND_SOC_DAPM_DIR_OUT] = wsink; + + path->connected = connected; + INIT_LIST_HEAD(&path->list); + INIT_LIST_HEAD(&path->list_kcontrol); + + if (wsource->is_supply || wsink->is_supply) + path->is_supply = 1; + + /* connect static paths */ + if (control == NULL) { + path->connect = 1; + } else { + switch (wsource->id) { + case snd_soc_dapm_demux: + ret = dapm_connect_mux(dapm, path, control, wsource); + if (ret) + goto err; + break; + default: + break; + } + + switch (wsink->id) { + case snd_soc_dapm_mux: + ret = dapm_connect_mux(dapm, path, control, wsink); + if (ret != 0) + goto err; + break; + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + ret = dapm_connect_mixer(dapm, path, control); + if (ret != 0) + goto err; + break; + default: + break; + } + } + + list_add(&path->list, &dapm->card->paths); + + snd_soc_dapm_for_each_direction(dir) + list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); + + snd_soc_dapm_for_each_direction(dir) { + dapm_update_widget_flags(path->node[dir]); + dapm_mark_dirty(path->node[dir], "Route added"); + } + + if (snd_soc_card_is_instantiated(dapm->card) && path->connect) + dapm_path_invalidate(path); + + return 0; +err: + kfree(path); + return ret; +} + static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, const char *ctrl_name) { @@ -618,13 +924,6 @@ static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) return dapm->component->name_prefix; } -static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg) -{ - if (!dapm->component) - return -EIO; - return snd_soc_component_read(dapm->component, reg); -} - static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, int reg, unsigned int mask, unsigned int value) { @@ -678,160 +977,60 @@ dapm_wcache_lookup(struct snd_soc_dapm_widget *w, const char *name) * the context is already at the same level. Furthermore it will not go through * the normal bias level sequencing, meaning any intermediate states between the * current and the target state will not be entered. - * - * Note that the change in bias level is only temporary and the next time - * snd_soc_dapm_sync() is called the state will be set to the level as - * determined by the DAPM core. The function is mainly intended to be used to - * used during probe or resume from suspend to power up the device so - * initialization can be done, before the DAPM core takes over. - */ -int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, - enum snd_soc_bias_level level) -{ - int ret = 0; - - if (dapm->component) - ret = snd_soc_component_set_bias_level(dapm->component, level); - - if (ret == 0) - dapm->bias_level = level; - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); - -/** - * snd_soc_dapm_set_bias_level - set the bias level for the system - * @dapm: DAPM context - * @level: level to configure - * - * Configure the bias (power) levels for the SoC audio device. - * - * Returns 0 for success else error. - */ -static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, - enum snd_soc_bias_level level) -{ - struct snd_soc_card *card = dapm->card; - int ret = 0; - - trace_snd_soc_bias_level_start(dapm, level); - - ret = snd_soc_card_set_bias_level(card, dapm, level); - if (ret != 0) - goto out; - - if (dapm != &card->dapm) - ret = snd_soc_dapm_force_bias_level(dapm, level); - - if (ret != 0) - goto out; - - ret = snd_soc_card_set_bias_level_post(card, dapm, level); -out: - trace_snd_soc_bias_level_done(dapm, level); - - return ret; -} - -/* connect mux widget to its interconnecting audio paths */ -static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_path *path, const char *control_name, - struct snd_soc_dapm_widget *w) -{ - const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int item; - int i; - - if (e->reg != SND_SOC_NOPM) { - unsigned int val; - val = soc_dapm_read(dapm, e->reg); - val = (val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); - } else { - /* since a virtual mux has no backing registers to - * decide which path to connect, it will try to match - * with the first enumeration. This is to ensure - * that the default mux choice (the first) will be - * correctly powered up during initialization. - */ - item = 0; - } - - i = match_string(e->texts, e->items, control_name); - if (i < 0) - return -ENODEV; - - path->name = e->texts[i]; - path->connect = (i == item); - return 0; - -} - -/* set up initial codec paths */ -static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i, - int nth_path) -{ - struct soc_mixer_control *mc = (struct soc_mixer_control *) - p->sink->kcontrol_news[i].private_value; - unsigned int reg = mc->reg; - unsigned int invert = mc->invert; - - if (reg != SND_SOC_NOPM) { - unsigned int shift = mc->shift; - unsigned int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int val = soc_dapm_read(p->sink->dapm, reg); - - /* - * The nth_path argument allows this function to know - * which path of a kcontrol it is setting the initial - * status for. Ideally this would support any number - * of paths and channels. But since kcontrols only come - * in mono and stereo variants, we are limited to 2 - * channels. - * - * The following code assumes for stereo controls the - * first path is the left channel, and all remaining - * paths are the right channel. - */ - if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) { - if (reg != mc->rreg) - val = soc_dapm_read(p->sink->dapm, mc->rreg); - val = (val >> mc->rshift) & mask; - } else { - val = (val >> shift) & mask; - } - if (invert) - val = max - val; - p->connect = !!val; - } else { - /* since a virtual mixer has no backing registers to - * decide which path to connect, it will try to match - * with initial state. This is to ensure - * that the default mixer choice will be - * correctly powered up during initialization. - */ - p->connect = invert; - } + * + * Note that the change in bias level is only temporary and the next time + * snd_soc_dapm_sync() is called the state will be set to the level as + * determined by the DAPM core. The function is mainly intended to be used to + * used during probe or resume from suspend to power up the device so + * initialization can be done, before the DAPM core takes over. + */ +int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + int ret = 0; + + if (dapm->component) + ret = snd_soc_component_set_bias_level(dapm->component, level); + + if (ret == 0) + dapm->bias_level = level; + + return ret; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); -/* connect mixer widget to its interconnecting audio paths */ -static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_path *path, const char *control_name) +/** + * snd_soc_dapm_set_bias_level - set the bias level for the system + * @dapm: DAPM context + * @level: level to configure + * + * Configure the bias (power) levels for the SoC audio device. + * + * Returns 0 for success else error. + */ +static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) { - int i, nth_path = 0; + struct snd_soc_card *card = dapm->card; + int ret = 0; - /* search for mixer kcontrol */ - for (i = 0; i < path->sink->num_kcontrols; i++) { - if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { - path->name = path->sink->kcontrol_news[i].name; - dapm_set_mixer_path_status(path, i, nth_path++); - return 0; - } - } - return -ENODEV; + trace_snd_soc_bias_level_start(dapm, level); + + ret = snd_soc_card_set_bias_level(card, dapm, level); + if (ret != 0) + goto out; + + if (dapm != &card->dapm) + ret = snd_soc_dapm_force_bias_level(dapm, level); + + if (ret != 0) + goto out; + + ret = snd_soc_card_set_bias_level_post(card, dapm, level); +out: + trace_snd_soc_bias_level_done(dapm, level); + + return ret; } static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, @@ -2783,210 +2982,6 @@ int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char } EXPORT_SYMBOL_GPL(snd_soc_dapm_widget_name_cmp); -/* - * dapm_update_widget_flags() - Re-compute widget sink and source flags - * @w: The widget for which to update the flags - * - * Some widgets have a dynamic category which depends on which neighbors they - * are connected to. This function update the category for these widgets. - * - * This function must be called whenever a path is added or removed to a widget. - */ -static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) -{ - enum snd_soc_dapm_direction dir; - struct snd_soc_dapm_path *p; - unsigned int ep; - - switch (w->id) { - case snd_soc_dapm_input: - /* On a fully routed card an input is never a source */ - if (w->dapm->card->fully_routed) - return; - ep = SND_SOC_DAPM_EP_SOURCE; - snd_soc_dapm_widget_for_each_source_path(w, p) { - if (p->source->id == snd_soc_dapm_micbias || - p->source->id == snd_soc_dapm_mic || - p->source->id == snd_soc_dapm_line || - p->source->id == snd_soc_dapm_output) { - ep = 0; - break; - } - } - break; - case snd_soc_dapm_output: - /* On a fully routed card a output is never a sink */ - if (w->dapm->card->fully_routed) - return; - ep = SND_SOC_DAPM_EP_SINK; - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if (p->sink->id == snd_soc_dapm_spk || - p->sink->id == snd_soc_dapm_hp || - p->sink->id == snd_soc_dapm_line || - p->sink->id == snd_soc_dapm_input) { - ep = 0; - break; - } - } - break; - case snd_soc_dapm_line: - ep = 0; - snd_soc_dapm_for_each_direction(dir) { - if (!list_empty(&w->edges[dir])) - ep |= SND_SOC_DAPM_DIR_TO_EP(dir); - } - break; - default: - return; - } - - w->is_ep = ep; -} - -static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, - const char *control) -{ - bool dynamic_source = false; - bool dynamic_sink = false; - - if (!control) - return 0; - - switch (source->id) { - case snd_soc_dapm_demux: - dynamic_source = true; - break; - default: - break; - } - - switch (sink->id) { - case snd_soc_dapm_mux: - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - dynamic_sink = true; - break; - default: - break; - } - - if (dynamic_source && dynamic_sink) { - dev_err(dapm->dev, - "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", - source->name, control, sink->name); - return -EINVAL; - } else if (!dynamic_source && !dynamic_sink) { - dev_err(dapm->dev, - "Control not supported for path %s -> [%s] -> %s\n", - source->name, control, sink->name); - return -EINVAL; - } - - return 0; -} - -static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, - const char *control, - int (*connected)(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink)) -{ - enum snd_soc_dapm_direction dir; - struct snd_soc_dapm_path *path; - int ret; - - if (wsink->is_supply && !wsource->is_supply) { - dev_err(dapm->dev, - "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", - wsource->name, wsink->name); - return -EINVAL; - } - - if (connected && !wsource->is_supply) { - dev_err(dapm->dev, - "connected() callback only supported for supply widgets (%s -> %s)\n", - wsource->name, wsink->name); - return -EINVAL; - } - - if (wsource->is_supply && control) { - dev_err(dapm->dev, - "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", - wsource->name, control, wsink->name); - return -EINVAL; - } - - ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); - if (ret) - return ret; - - path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); - if (!path) - return -ENOMEM; - - path->node[SND_SOC_DAPM_DIR_IN] = wsource; - path->node[SND_SOC_DAPM_DIR_OUT] = wsink; - - path->connected = connected; - INIT_LIST_HEAD(&path->list); - INIT_LIST_HEAD(&path->list_kcontrol); - - if (wsource->is_supply || wsink->is_supply) - path->is_supply = 1; - - /* connect static paths */ - if (control == NULL) { - path->connect = 1; - } else { - switch (wsource->id) { - case snd_soc_dapm_demux: - ret = dapm_connect_mux(dapm, path, control, wsource); - if (ret) - goto err; - break; - default: - break; - } - - switch (wsink->id) { - case snd_soc_dapm_mux: - ret = dapm_connect_mux(dapm, path, control, wsink); - if (ret != 0) - goto err; - break; - case snd_soc_dapm_switch: - case snd_soc_dapm_mixer: - case snd_soc_dapm_mixer_named_ctl: - ret = dapm_connect_mixer(dapm, path, control); - if (ret != 0) - goto err; - break; - default: - break; - } - } - - list_add(&path->list, &dapm->card->paths); - - snd_soc_dapm_for_each_direction(dir) - list_add(&path->list_node[dir], &path->node[dir]->edges[dir]); - - snd_soc_dapm_for_each_direction(dir) { - dapm_update_widget_flags(path->node[dir]); - dapm_mark_dirty(path->node[dir], "Route added"); - } - - if (snd_soc_card_is_instantiated(dapm->card) && path->connect) - dapm_path_invalidate(path); - - return 0; -err: - kfree(path); - return ret; -} - static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { -- GitLab From f02ccc8c0b99382807bac0065918a90ba974e9ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:51 +0000 Subject: [PATCH 491/868] ASoC: soc-dapm: reordering header definitions Because header defined randomly, it needs name definitions on top of soc-dapm.h. it is not needed if definitions are implemented in correct order. This patch has big change from change-line point of view, but is just reordering, nothing changed in meaning. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87plekhit0.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 235 ++++++++++++++++++--------------------- 1 file changed, 110 insertions(+), 125 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6d854c727bca8..5aeb0822ce0bf 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -16,9 +16,10 @@ #include struct device; +struct regulator; +struct soc_enum; struct snd_pcm_substream; struct snd_soc_pcm_runtime; -struct soc_enum; /* widget has no PM register bit */ #define SND_SOC_NOPM -1 @@ -399,17 +400,6 @@ struct soc_enum; /* regulator widget flags */ #define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */ -struct snd_soc_dapm_widget; -enum snd_soc_dapm_type; -struct snd_soc_dapm_path; -struct snd_soc_dapm_pin; -struct snd_soc_dapm_route; -struct snd_soc_dapm_context; -struct regulator; -struct snd_soc_dapm_widget_list; -struct snd_soc_dapm_update; -enum snd_soc_dapm_direction; - /* * Bias levels * @@ -428,104 +418,6 @@ enum snd_soc_bias_level { SND_SOC_BIAS_ON = 3, }; -int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); - -/* dapm controls */ -int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uncontrol); -int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uncontrol); -int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uncontrol); -int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *uncontrol); -int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget, unsigned int num); -struct snd_soc_dapm_widget *snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); -struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_widget *widget); -int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); -void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); -int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); -void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); - -int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); -int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s); - -/* dapm path setup */ -int snd_soc_dapm_new_widgets(struct snd_soc_card *card); -void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); -void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, - struct snd_soc_card *card, struct snd_soc_component *component); -int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route, int num); -int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route, int num); -void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); - -/* dapm events */ -void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event); -void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream); -void snd_soc_dapm_shutdown(struct snd_soc_card *card); - -/* external DAPM widget events */ -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect, struct snd_soc_dapm_update *update); -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, - struct snd_soc_dapm_update *update); - -/* dapm sys fs - used by the core */ -extern struct attribute *soc_dapm_dev_attrs[]; -void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, struct dentry *parent); - -/* dapm audio pin control and status */ -int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); -int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); -int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); -int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); -unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); -void dapm_mark_endpoints_dirty(struct snd_soc_card *card); - -/* - * Marks the specified pin as being not connected, disabling it along - * any parent or child widgets. At present this is identical to - * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do - * additional things such as disabling controls which only affect - * paths through the pin. - */ -#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin -#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked - -/* dapm path query */ -int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, - struct snd_soc_dapm_widget_list **list, - bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)); -void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list); - -struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kcontrol); -struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol); - -int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); - /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ @@ -717,11 +609,6 @@ struct snd_soc_dapm_widget_list { struct snd_soc_dapm_widget *widgets[] __counted_by(num_widgets); }; -#define for_each_dapm_widgets(list, i, widget) \ - for ((i) = 0; \ - (i) < list->num_widgets && (widget = list->widgets[i]); \ - (i)++) - struct snd_soc_dapm_stats { int power_checks; int path_checks; @@ -733,6 +620,114 @@ struct snd_soc_dapm_pinctrl_priv { const char *sleep_state; }; +enum snd_soc_dapm_direction { + SND_SOC_DAPM_DIR_IN, + SND_SOC_DAPM_DIR_OUT +}; + +#define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x) + +#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) +#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) + +int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); + +/* dapm controls */ +int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uncontrol); +int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uncontrol); +int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uncontrol); +int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uncontrol); +int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget, unsigned int num); +struct snd_soc_dapm_widget *snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); +struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); +int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); +int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); +void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); + +int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai); +int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s); + +/* dapm path setup */ +int snd_soc_dapm_new_widgets(struct snd_soc_card *card); +void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); +void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, + struct snd_soc_card *card, struct snd_soc_component *component); +int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num); +int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num); +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); + +/* dapm events */ +void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, int event); +void snd_soc_dapm_stream_stop(struct snd_soc_pcm_runtime *rtd, int stream); +void snd_soc_dapm_shutdown(struct snd_soc_card *card); + +/* external DAPM widget events */ +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int connect, struct snd_soc_dapm_update *update); +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update); + +/* dapm sys fs - used by the core */ +extern struct attribute *soc_dapm_dev_attrs[]; +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, struct dentry *parent); + +/* dapm audio pin control and status */ +int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); +unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); +void dapm_mark_endpoints_dirty(struct snd_soc_card *card); + +/* + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin[_unlocked]() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + */ +#define snd_soc_dapm_nc_pin snd_soc_dapm_disable_pin +#define snd_soc_dapm_nc_pin_unlocked snd_soc_dapm_disable_pin_unlocked + +/* dapm path query */ +int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, + struct snd_soc_dapm_widget_list **list, + bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)); +void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list); + +struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(struct snd_kcontrol *kcontrol); + +int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); + +#define for_each_dapm_widgets(list, i, widget) \ + for ((i) = 0; \ + (i) < list->num_widgets && (widget = list->widgets[i]); \ + (i)++) + /** * snd_soc_dapm_init_bias_level() - Initialize DAPM bias level * @dapm: The DAPM context to initialize @@ -764,16 +759,6 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( return dapm->bias_level; } -enum snd_soc_dapm_direction { - SND_SOC_DAPM_DIR_IN, - SND_SOC_DAPM_DIR_OUT -}; - -#define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x) - -#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) -#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) - /** * snd_soc_dapm_widget_for_each_path - Iterates over all paths in the * specified direction of a widget -- GitLab From fbd09117a38e5ff477040629f7b5fc442883746a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:10:57 +0000 Subject: [PATCH 492/868] ASoC: soc-dapm: use component instead of cmpnt Use normal "component" instead of short "cmpnt" Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6u4hisu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index babb642c96558..d4cf047bc93e0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2628,10 +2628,10 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); -static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, +static ssize_t dapm_widget_show_component(struct snd_soc_component *component, char *buf, int count) { - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dapm_widget *w; char *state = "not set"; @@ -2639,10 +2639,10 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, * we're checking for that case specifically here but in future * we will ensure that the dummy component looks like others. */ - if (!cmpnt->card) + if (!component->card) return 0; - for_each_card_widgets(cmpnt->card, w) { + for_each_card_widgets(component->card, w) { if (w->dapm != dapm) continue; @@ -2703,9 +2703,9 @@ static ssize_t dapm_widget_show(struct device *dev, snd_soc_dapm_mutex_lock_root(rtd->card); for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_soc_component *cmpnt = codec_dai->component; + struct snd_soc_component *component = codec_dai->component; - count = dapm_widget_show_component(cmpnt, buf, count); + count = dapm_widget_show_component(component, buf, count); } snd_soc_dapm_mutex_unlock(rtd->card); -- GitLab From 1ac23653840f784d0f5846ac253d04ecef9a26f6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:11:03 +0000 Subject: [PATCH 493/868] ASoC: soc-dapm: use common name for dapm Let's use "dapm", instead of "d". This is prepare for dapm cleanup. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ms9ohisp.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 58 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d4cf047bc93e0..16f8eff42835a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1985,29 +1985,29 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up */ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) { - struct snd_soc_dapm_context *d = data; + struct snd_soc_dapm_context *dapm = data; int ret; /* If we're off and we're not supposed to go into STANDBY */ - if (d->bias_level == SND_SOC_BIAS_OFF && - d->target_bias_level != SND_SOC_BIAS_OFF) { - if (d->dev && cookie) - pm_runtime_get_sync(d->dev); + if (dapm->bias_level == SND_SOC_BIAS_OFF && + dapm->target_bias_level != SND_SOC_BIAS_OFF) { + if (dapm->dev && cookie) + pm_runtime_get_sync(dapm->dev); - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); if (ret != 0) - dev_err(d->dev, + dev_err(dapm->dev, "ASoC: Failed to turn on bias: %d\n", ret); } /* Prepare for a transition to ON or away from ON */ - if ((d->target_bias_level == SND_SOC_BIAS_ON && - d->bias_level != SND_SOC_BIAS_ON) || - (d->target_bias_level != SND_SOC_BIAS_ON && - d->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); + if ((dapm->target_bias_level == SND_SOC_BIAS_ON && + dapm->bias_level != SND_SOC_BIAS_ON) || + (dapm->target_bias_level != SND_SOC_BIAS_ON && + dapm->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); if (ret != 0) - dev_err(d->dev, + dev_err(dapm->dev, "ASoC: Failed to prepare bias: %d\n", ret); } } @@ -2017,37 +2017,37 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) */ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) { - struct snd_soc_dapm_context *d = data; + struct snd_soc_dapm_context *dapm = data; int ret; /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && - (d->target_bias_level == SND_SOC_BIAS_STANDBY || - d->target_bias_level == SND_SOC_BIAS_OFF)) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (dapm->bias_level == SND_SOC_BIAS_PREPARE && + (dapm->target_bias_level == SND_SOC_BIAS_STANDBY || + dapm->target_bias_level == SND_SOC_BIAS_OFF)) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to apply standby bias: %d\n", ret); } /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && - d->target_bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); + if (dapm->bias_level == SND_SOC_BIAS_STANDBY && + dapm->target_bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to turn off bias: %d\n", ret); - if (d->dev && cookie) - pm_runtime_put(d->dev); + if (dapm->dev && cookie) + pm_runtime_put(dapm->dev); } /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && - d->target_bias_level == SND_SOC_BIAS_ON) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); + if (dapm->bias_level == SND_SOC_BIAS_PREPARE && + dapm->target_bias_level == SND_SOC_BIAS_ON) { + ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_ON); if (ret != 0) - dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n", + dev_err(dapm->dev, "ASoC: Failed to apply active bias: %d\n", ret); } } -- GitLab From 805c019fbb94795e391974f673c5b3e57b825f6d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:11:08 +0000 Subject: [PATCH 494/868] ASoC: soc-dapm: add prefix on dapm_mark_endpoints_dirty() dapm_mark_endpoints_dirty() is global function. Let's add snd_soc_ prefix. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ldp8hisj.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-core.c | 6 +++--- sound/soc/soc-dapm.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 5aeb0822ce0bf..ee77a80765c6c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -700,7 +700,7 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol); -void dapm_mark_endpoints_dirty(struct snd_soc_card *card); +void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card); /* * Marks the specified pin as being not connected, disabling it along diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 67bebc339148b..b59f612de4e5a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -681,7 +681,7 @@ int snd_soc_suspend(struct device *dev) soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_SUSPEND); /* Recheck all endpoints too, their state is affected by suspend */ - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* suspend all COMPONENTs */ @@ -778,7 +778,7 @@ static void soc_resume_deferred(struct work_struct *work) dev_dbg(card->dev, "ASoC: resume work completed\n"); /* Recheck all endpoints too, their state is affected by suspend */ - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* userspace can access us now we are back as we were before */ @@ -2286,7 +2286,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) } card->instantiated = 1; - dapm_mark_endpoints_dirty(card); + snd_soc_dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); /* deactivate pins to sleep state */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 16f8eff42835a..d612dfbd245e1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -283,7 +283,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) dapm_widget_invalidate_output_paths(p->source); } -void dapm_mark_endpoints_dirty(struct snd_soc_card *card) +void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; -- GitLab From 9d33f9ca4404e532453a0305ce11bf76a9945c5d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:11:15 +0000 Subject: [PATCH 495/868] ASoC: soc-dapm: add prefix on dapm_xxx_event() dapm_xxx_event() is global function. Let's add snd_soc_ prefix. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87jz4shisc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 12 ++++++------ sound/soc/mediatek/mt8188/mt8188-mt6359.c | 4 ++-- sound/soc/soc-dapm.c | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ee77a80765c6c..be5ecc276562c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -295,7 +295,7 @@ struct snd_soc_pcm_runtime; #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ (struct snd_soc_dapm_widget) { \ .id = snd_soc_dapm_clock_supply, .name = wname, \ - .reg = SND_SOC_NOPM, .event = dapm_clock_event, \ + .reg = SND_SOC_NOPM, .event = snd_soc_dapm_clock_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } /* generic widgets */ @@ -312,7 +312,7 @@ struct snd_soc_pcm_runtime; #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \ (struct snd_soc_dapm_widget) { \ .id = snd_soc_dapm_regulator_supply, .name = wname, \ - .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ + .reg = SND_SOC_NOPM, .shift = wdelay, .event = snd_soc_dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ .on_val = wflags} #define SND_SOC_DAPM_PINCTRL(wname, active, sleep) \ @@ -320,7 +320,7 @@ struct snd_soc_pcm_runtime; .id = snd_soc_dapm_pinctrl, .name = wname, \ .priv = (&(struct snd_soc_dapm_pinctrl_priv) \ { .active_state = active, .sleep_state = sleep,}), \ - .reg = SND_SOC_NOPM, .event = dapm_pinctrl_event, \ + .reg = SND_SOC_NOPM, .event = snd_soc_dapm_pinctrl_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } @@ -630,9 +630,9 @@ enum snd_soc_dapm_direction { #define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) #define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) -int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); /* dapm controls */ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index a2a76b6df6311..ea814a0f726d6 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -408,7 +408,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) } if (pin_w) - dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU); + snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU); else dev_dbg(afe->dev, "%s(), no pinmux widget, please check if default on\n", __func__); @@ -510,7 +510,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i]; if (pin_w) - dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD); + snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD); dev_dbg(afe->dev, "%s(), end, calibration ok %d\n", __func__, param->mtkaif_calibration_ok); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d612dfbd245e1..e8bd83fb9ef49 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1537,8 +1537,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets); /* * Handler for regulator supply widget. */ -int dapm_regulator_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { int ret; @@ -1566,13 +1566,13 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_disable_deferred(w->regulator, w->shift); } } -EXPORT_SYMBOL_GPL(dapm_regulator_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_regulator_event); /* * Handler for pinctrl widget. */ -int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_dapm_pinctrl_priv *priv = w->priv; struct pinctrl *p = w->pinctrl; @@ -1591,13 +1591,13 @@ int dapm_pinctrl_event(struct snd_soc_dapm_widget *w, return pinctrl_select_state(p, s); } -EXPORT_SYMBOL_GPL(dapm_pinctrl_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_pinctrl_event); /* * Handler for clock supply widget. */ -int dapm_clock_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { if (!w->clk) return -EIO; @@ -1613,7 +1613,7 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w, return 0; } -EXPORT_SYMBOL_GPL(dapm_clock_event); +EXPORT_SYMBOL_GPL(snd_soc_dapm_clock_event); static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) { @@ -3668,7 +3668,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, } /* set to sleep_state when initializing */ - dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); + snd_soc_dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD); break; case snd_soc_dapm_clock_supply: w->clk = devm_clk_get(dapm->dev, widget->name); -- GitLab From 08dc0f5cc26a203e8008c38d9b436c079e7dbb45 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 1 Jul 2025 00:11:23 +0000 Subject: [PATCH 496/868] ASoC: soc-dapm: add prefix on soc_dapm_dev_attrs soc_dapm_dev_attrs is global variable. Let's add snd_soc_ prefix. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ikkchis6.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-core.c | 2 +- sound/soc/soc-dapm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index be5ecc276562c..0b5c7e6a90c81 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -685,7 +685,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_update *update); /* dapm sys fs - used by the core */ -extern struct attribute *soc_dapm_dev_attrs[]; +extern struct attribute *snd_soc_dapm_dev_attrs[]; void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, struct dentry *parent); /* dapm audio pin control and status */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b59f612de4e5a..ebe94956df44b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -112,7 +112,7 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj, } static const struct attribute_group soc_dapm_dev_group = { - .attrs = soc_dapm_dev_attrs, + .attrs = snd_soc_dapm_dev_attrs, .is_visible = soc_dev_attr_is_visible, }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e8bd83fb9ef49..b8a5875378c87 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2715,7 +2715,7 @@ static ssize_t dapm_widget_show(struct device *dev, static DEVICE_ATTR_RO(dapm_widget); -struct attribute *soc_dapm_dev_attrs[] = { +struct attribute *snd_soc_dapm_dev_attrs[] = { &dev_attr_dapm_widget.attr, NULL }; -- GitLab From c8aea83c735ec5a853db495592a1b6b5e6db859b Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Thu, 3 Jul 2025 13:04:21 -0700 Subject: [PATCH 497/868] ACPI: APEI: EINJ: Fix trigger actions The trigger events are in BIOS memory immediately following the acpi_einj_trigger structure. These were not copied to regular kernel memory for use by apei_exec_ctx_init() so injections in "notrigger=0" mode failed with a message like this: APEI: Invalid action table, unknown instruction type: 123 Fix by allocating a "table_size" block of memory and copying the whole table for use in the rest of the trigger flow. Fixes: 1a35c88302a3 ("ACPI: APEI: EINJ: Fix kernel test sparse warnings") Reported-by: Yi1 Lai Signed-off-by: Tony Luck Link: https://patch.msgid.link/20250703200421.28012-1-tony.luck@intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/einj-core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 3d37978418e87..bf8dc92a373af 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -394,6 +394,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, u64 param1, u64 param2) { struct acpi_einj_trigger trigger_tab; + struct acpi_einj_trigger *full_trigger_tab; struct apei_exec_context trigger_ctx; struct apei_resources trigger_resources; struct acpi_whea_header *trigger_entry; @@ -430,6 +431,9 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, rc = -EIO; table_size = trigger_tab.table_size; + full_trigger_tab = kmalloc(table_size, GFP_KERNEL); + if (!full_trigger_tab) + goto out_rel_header; r = request_mem_region(trigger_paddr + sizeof(trigger_tab), table_size - sizeof(trigger_tab), "APEI EINJ Trigger Table"); @@ -437,7 +441,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", (unsigned long long)trigger_paddr + sizeof(trigger_tab), (unsigned long long)trigger_paddr + table_size - 1); - goto out_rel_header; + goto out_free_trigger_tab; } iounmap(p); p = ioremap_cache(trigger_paddr, table_size); @@ -445,9 +449,9 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, pr_err("Failed to map trigger table!\n"); goto out_rel_entry; } - memcpy_fromio(&trigger_tab, p, sizeof(trigger_tab)); + memcpy_fromio(full_trigger_tab, p, table_size); trigger_entry = (struct acpi_whea_header *) - ((char *)&trigger_tab + sizeof(struct acpi_einj_trigger)); + ((char *)full_trigger_tab + sizeof(struct acpi_einj_trigger)); apei_resources_init(&trigger_resources); apei_exec_ctx_init(&trigger_ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), @@ -469,7 +473,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, apei_resources_init(&addr_resources); trigger_param_region = einj_get_trigger_parameter_region( - &trigger_tab, param1, param2); + full_trigger_tab, param1, param2); if (trigger_param_region) { rc = apei_resources_add(&addr_resources, trigger_param_region->address, @@ -500,6 +504,8 @@ out_fini: out_rel_entry: release_mem_region(trigger_paddr + sizeof(trigger_tab), table_size - sizeof(trigger_tab)); +out_free_trigger_tab: + kfree(full_trigger_tab); out_rel_header: release_mem_region(trigger_paddr, sizeof(trigger_tab)); out: -- GitLab From e71b59b4817b845f638225f29405a5435c047d72 Mon Sep 17 00:00:00 2001 From: Eslam Khafagy Date: Fri, 4 Jul 2025 03:40:00 +0300 Subject: [PATCH 498/868] ACPI: fan: Replace sprintf() with sysfs_emit() Replace sprintf() with sysfs_emit() in function show_fine_grain_control() in according to Documentation/filesystems/sysfs.rst. Link: https://lore.kernel.org/all/20250621055200.166361-1-abdelrahmanfekry375@gmail.com/ Signed-off-by: Eslam Khafagy Link: https://patch.msgid.link/20250704004002.70839-1-eslam.medhat1993@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan_attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c index 6a53da3d6d826..c1afb7b5ed3dc 100644 --- a/drivers/acpi/fan_attr.c +++ b/drivers/acpi/fan_attr.c @@ -67,7 +67,7 @@ static ssize_t show_fine_grain_control(struct device *dev, struct device_attribu struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); struct acpi_fan *fan = acpi_driver_data(acpi_dev); - return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl); + return sysfs_emit(buf, "%d\n", fan->fif.fine_grain_ctrl); } int acpi_fan_create_attributes(struct acpi_device *device) -- GitLab From 3db5648c4d608b5483470efc1da9780b081242dd Mon Sep 17 00:00:00 2001 From: Zhu Qiyu Date: Fri, 4 Jul 2025 01:41:04 +0000 Subject: [PATCH 499/868] ACPI: PRM: Reduce unnecessary printing to avoid user confusion Commit 088984c8d54c ("ACPI: PRM: Find EFI_MEMORY_RUNTIME block for PRM handler and context") introduced non-essential printing "Failed to find VA for GUID: xxxx, PA: 0x0" which may confuse users to think that something wrong is going on while it is not the case. According to the PRM Spec Section 4.1.2 [1], both static data buffer address and ACPI parameter buffer address may be NULL if they are not needed, so there is no need to print out the "Failed to find VA ... " in those cases. Link: https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf # [1] Signed-off-by: Zhu Qiyu Link: https://patch.msgid.link/20250704014104.82524-1-qiyuzhu2@amd.com [ rjw: Edits in new comments, subject and changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/prmt.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c index e549914a636c6..be033bbb126a4 100644 --- a/drivers/acpi/prmt.c +++ b/drivers/acpi/prmt.c @@ -85,8 +85,6 @@ static u64 efi_pa_va_lookup(efi_guid_t *guid, u64 pa) } } - pr_warn("Failed to find VA for GUID: %pUL, PA: 0x%llx", guid, pa); - return 0; } @@ -154,13 +152,37 @@ acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end) guid_copy(&th->guid, (guid_t *)handler_info->handler_guid); th->handler_addr = (void *)efi_pa_va_lookup(&th->guid, handler_info->handler_address); + /* + * Print a warning message if handler_addr is zero which is not expected to + * ever happen. + */ + if (unlikely(!th->handler_addr)) + pr_warn("Failed to find VA of handler for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->handler_address); th->static_data_buffer_addr = efi_pa_va_lookup(&th->guid, handler_info->static_data_buffer_address); + /* + * According to the PRM specification, static_data_buffer_address can be zero, + * so avoid printing a warning message in that case. Otherwise, if the + * return value of efi_pa_va_lookup() is zero, print the message. + */ + if (unlikely(!th->static_data_buffer_addr && handler_info->static_data_buffer_address)) + pr_warn("Failed to find VA of static data buffer for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->static_data_buffer_address); th->acpi_param_buffer_addr = efi_pa_va_lookup(&th->guid, handler_info->acpi_param_buffer_address); + /* + * According to the PRM specification, acpi_param_buffer_address can be zero, + * so avoid printing a warning message in that case. Otherwise, if the + * return value of efi_pa_va_lookup() is zero, print the message. + */ + if (unlikely(!th->acpi_param_buffer_addr && handler_info->acpi_param_buffer_address)) + pr_warn("Failed to find VA of acpi param buffer for GUID: %pUL, PA: 0x%llx", + &th->guid, handler_info->acpi_param_buffer_address); + } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info))); return 0; -- GitLab From c9d52116c5d4ce6ef004fb6a06f98000e71d1b90 Mon Sep 17 00:00:00 2001 From: Sumeet Pawnikar Date: Sat, 5 Jul 2025 16:30:04 +0530 Subject: [PATCH 500/868] ACPI: fan: Update debug message in fan_get_state_acpi4() Update invalid control value returned debug print with appropriate message as no matching fps control value for checking fan fps count condition. Signed-off-by: Sumeet Pawnikar Link: https://patch.msgid.link/20250705110005.4343-1-sumeet4linux@gmail.com> [ rjw: Subject edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 8ad12ad3aaaf4..095502086b412 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -102,7 +102,7 @@ match_fps: break; } if (i == fan->fps_count) { - dev_dbg(&device->dev, "Invalid control value returned\n"); + dev_dbg(&device->dev, "No matching fps control value\n"); return -EINVAL; } -- GitLab From 5054740e0092aac528c0589251f612b3b41c9e7b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:57:16 +0800 Subject: [PATCH 501/868] regulator: sy8827n: make enable gpio NONEXCLUSIVE On some platforms, the sy8827n enable gpio may also be used for other purpose, so make it NONEXCLUSIVE to support this case. Signed-off-by: Jisheng Zhang Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20250629095716.841-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/sy8827n.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/sy8827n.c b/drivers/regulator/sy8827n.c index f11ff38b36c94..0b811514782f5 100644 --- a/drivers/regulator/sy8827n.c +++ b/drivers/regulator/sy8827n.c @@ -140,7 +140,8 @@ static int sy8827n_i2c_probe(struct i2c_client *client) return -EINVAL; } - di->en_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + di->en_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(di->en_gpio)) return PTR_ERR(di->en_gpio); -- GitLab From a3c3e84fc495dd983374f041e145e13df3525a15 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 8 Jul 2025 02:46:42 +0000 Subject: [PATCH 502/868] ASoC: rt700: don't set dapm->bias_level snd_soc_component_set_bias_level() (A) which will call .set_bias_level() callback (B) will be called from snd_soc_dapm_force_bias_level() (C) only, and it sets dapm->bias_level (D) inside. No need to set it by each driver. Remove it. (A) int snd_soc_component_set_bias_level(...) { ... if (component->driver->set_bias_level) (B) ret = component->driver->set_bias_level(...); ... } (C) int snd_soc_dapm_force_bias_level(...) { ... if (dapm->component) (A) ret = snd_soc_component_set_bias_level(...); if (ret == 0) (D) dapm->bias_level = level; ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzyrmmb2.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt700.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 434b926f96c83..f817209316c25 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -862,7 +862,7 @@ static int rt700_set_bias_level(struct snd_soc_component *component, default: break; } - dapm->bias_level = level; + return 0; } -- GitLab From 67bdd67aedcec8c63e3158c3c82991fbde0c4d22 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 8 Jul 2025 02:46:46 +0000 Subject: [PATCH 503/868] ASoC: rt715: don't set dapm->bias_level snd_soc_component_set_bias_level() (A) which will call .set_bias_level() callback (B) will be called from snd_soc_dapm_force_bias_level() (C) only, and it sets dapm->bias_level (D) inside. No need to set it by each driver. Remove it. (A) int snd_soc_component_set_bias_level(...) { ... if (component->driver->set_bias_level) (B) ret = component->driver->set_bias_level(...); ... } (C) int snd_soc_dapm_force_bias_level(...) { ... if (dapm->component) (A) ret = snd_soc_component_set_bias_level(...); if (ret == 0) (D) dapm->bias_level = level; ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87plebmmax.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 2cf4618520918..06ac2b56d9e47 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -775,7 +775,7 @@ static int rt715_set_bias_level(struct snd_soc_component *component, default: break; } - dapm->bias_level = level; + return 0; } -- GitLab From 2813f535b5847771d9e56df678c523a7e64f860e Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 8 Jul 2025 16:00:26 +0800 Subject: [PATCH 504/868] ASoC: Intel: soc-acpi: Add entry for sof_es8336 in PTL match table. Adding ES83x6 I2S codec support for PTL platforms and entry in match table. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250708080030.1257790-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index eae75f3f0fa40..ff4f2fbf9271d 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -21,6 +21,11 @@ static const struct snd_soc_acpi_codecs ptl_rt5682_rt5682s_hp = { .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, }; +static const struct snd_soc_acpi_codecs ptl_essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { { .comp_ids = &ptl_rt5682_rt5682s_hp, @@ -29,6 +34,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, }, + { + .comp_ids = &ptl_essx_83x6, + .drv_name = "sof-essx8336", + .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_machines); -- GitLab From fb00ab1f39369e49d25c74f0d41e4c1ec2f12576 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 8 Jul 2025 16:00:27 +0800 Subject: [PATCH 505/868] ASoC: Intel: soc-acpi: Add entry for HDMI_In capture support in PTL match table Adding HDMI-In capture via I2S feature support in PTL platform. Signed-off-by: Balamurugan C Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250708080030.1257790-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_es8336.c | 10 ++++++++++ sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index a0b3679b17b42..1211a2b8a2a2c 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -826,6 +826,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "ptl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index ff4f2fbf9271d..67f1091483dce 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -26,6 +26,11 @@ static const struct snd_soc_acpi_codecs ptl_essx_83x6 = { .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; +static const struct snd_soc_acpi_codecs ptl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { { .comp_ids = &ptl_rt5682_rt5682s_hp, @@ -34,6 +39,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME | SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME, }, + { + .comp_ids = &ptl_essx_83x6, + .drv_name = "ptl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &ptl_lt6911_hdmi, + .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &ptl_essx_83x6, .drv_name = "sof-essx8336", -- GitLab From e149d870687a5cfd702b700d81ae75ec6f41dd57 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Tue, 8 Jul 2025 16:00:28 +0800 Subject: [PATCH 506/868] ASoC: Intel: soc-acpi-intel-ptl-match: add support ptl-rt721-l0 This patch adds support for rt721 on Soundwire Link 0. Signed-off-by: Mac Chiang Reviewed-by: Liam Girdwood Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250708080030.1257790-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-ptl-match.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index 67f1091483dce..a8fee8cf49136 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -355,6 +355,15 @@ static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = { + { + .adr = 0x000030025d072101ull, + .num_endpoints = ARRAY_SIZE(rt_mf_endpoints), + .endpoints = rt_mf_endpoints, + .name_prefix = "rt721" + } +}; + static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = { { .adr = 0x000330025d072101ull, @@ -473,6 +482,15 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l3[] = { {} }; +static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt721_0_single_adr), + .adr_d = rt721_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = { { .mask = BIT(0), @@ -660,6 +678,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt711.tplg", }, + { + .link_mask = BIT(0), + .links = ptl_rt721_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt721.tplg", + .get_function_tplg_files = sof_sdw_get_tplg_files, + }, { .link_mask = BIT(0), .links = ptl_rt722_only, -- GitLab From 86ccd4d3e8bc9eeb5dde4080fcc67e0505d1d2c6 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Tue, 8 Jul 2025 16:00:29 +0800 Subject: [PATCH 507/868] ASoC: Intel: soc-acpi-intel-lnl-match: add rt1320_l12_rt714_l0 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch add acpi matching support for the rt1320 left and right amplifiers on soundwire link 1 and 2, and the rt714 dmic on soundwire link 0. Signed-off-by: Mac Chiang Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250708080030.1257790-5-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- .../intel/common/soc-acpi-intel-lnl-match.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 558dc4c912397..937a74a5d5237 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -419,6 +419,15 @@ static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = { + { + .adr = 0x000231025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = { { .adr = 0x000130025D132001ull, @@ -609,6 +618,25 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt1318_l12_rt714_l0[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_sdw_rt1320_l12_rt714_l0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group2_adr), + .adr_d = rt1320_1_group2_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_group2_adr), + .adr_d = rt1320_2_group2_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = { { .mask = BIT(0), @@ -740,6 +768,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg" }, + { + .link_mask = GENMASK(2, 0), + .links = lnl_sdw_rt1320_l12_rt714_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg" + }, { .link_mask = BIT(0) | BIT(1), .links = lnl_sdw_rt713_l0_rt1318_l1, -- GitLab From 68e4dadacb7faa393b532b41bbf99a2dbfec3b1b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 8 Jul 2025 17:47:29 +0200 Subject: [PATCH 508/868] ASoC: img: Imagination Technologies sound should depend on MIPS Before, all Imagination sound symbols were gated by the SND_SOC_IMG symbol, offering the user a simple option to hide them all. After the removal of this gate symbol, all symbols are exposed to the user, even when configuring a kernel for a non-Imagination platform. Fix this by adding a dependency on MIPS, to prevent asking the user about these drivers when configuring a kernel for a different architecture. Fixes: b13f7eef9ff82e01 ("ASoC: img: Standardize ASoC menu") Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/242832f225ae68018111648ea9934dc059741567.1751989463.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- sound/soc/img/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index 9a4cba6fdb505..22b75a8144a18 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Imagination Technologies" + depends on MIPS || COMPILE_TEST config SND_SOC_IMG_I2S_IN tristate "Imagination I2S Input Device Driver" -- GitLab From defe01abfb7f5c5bd53c723b8577d4fcd64faa5a Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 8 Jul 2025 21:16:37 -0500 Subject: [PATCH 509/868] spi: stm32-ospi: Use of_reserved_mem_region_to_resource() for "memory-region" Use the newly added of_reserved_mem_region_to_resource() function to handle "memory-region" properties. Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20250709021638.2047365-1-robh@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 2829535e5cd4f..72baa402a2c3a 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -766,9 +766,7 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_ospi *ospi = platform_get_drvdata(pdev); - struct resource *res; - struct reserved_mem *rmem = NULL; - struct device_node *node; + struct resource *res, _res; int ret; ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -820,17 +818,13 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) goto err_dma; } - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (node) - rmem = of_reserved_mem_lookup(node); - of_node_put(node); - - if (rmem) { - ospi->mm_size = rmem->size; - ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size); + res = &_res; + ret = of_reserved_mem_region_to_resource(dev->of_node, 0, res); + if (!ret) { + ospi->mm_size = resource_size(res); + ospi->mm_base = devm_ioremap_resource(dev, res); if (!ospi->mm_base) { - dev_err(dev, "unable to map memory region: %pa+%pa\n", - &rmem->base, &rmem->size); + dev_err(dev, "unable to map memory region: %pR\n", res); ret = -ENOMEM; goto err_dma; } -- GitLab From 428f6f3a56ac85f37a07a3fe5149b593185d5c4c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 8 Jul 2025 16:41:01 -0700 Subject: [PATCH 510/868] platform/x86/intel/pmt/discovery: Fix size_t specifiers for 32-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building i386 allmodconfig, there are two warnings in the newly added discovery code: drivers/platform/x86/intel/pmt/discovery.c: In function 'pmt_feature_get_feature_table': drivers/platform/x86/intel/pmt/discovery.c:427:35: error: format '%ld' expects argument of type 'long int', but argument 2 has type 'size_t' {aka 'unsigned int'} [-Werror=format=] 427 | if (WARN(size > res_size, "Bad table size %ld > %pa", size, &res_size)) | ^~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~ | | | size_t {aka unsigned int} ... drivers/platform/x86/intel/pmt/discovery.c:427:53: note: format string is defined here 427 | if (WARN(size > res_size, "Bad table size %ld > %pa", size, &res_size)) | ~~^ | | | long int | %d drivers/platform/x86/intel/pmt/discovery-kunit.c: In function 'validate_pmt_regions': include/linux/kern_levels.h:5:25: error: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'size_t' {aka 'unsigned int'} [-Werror=format=] ... drivers/platform/x86/intel/pmt/discovery-kunit.c:35:17: note: in expansion of macro 'kunit_info' 35 | kunit_info(test, "\t\taddr=%p, size=%lu, num_rmids=%u", region->addr, region->size, | ^~~~~~~~~~ size_t is 'unsigned long' for 64-bit platforms but 'unsigned int' for 32-bit platforms, so '%ld' is not correct. Use the proper size_t specifier, '%zu', to resolve the warnings on 32-bit platforms while not affecting 64-bit platforms. Reported-by: Randy Dunlap Reported-by: kernelci.org bot Fixes: d9a078809356 ("platform/x86/intel/pmt: Add PMT Discovery driver") Fixes: b9707d46a959 ("platform/x86/intel/pmt: KUNIT test for PMT Enhanced Discovery API") Closes: https://lore.kernel.org/all/CACo-S-29Degjym-azsJNSd1yofLOB2_Rf5xpa9b7L-14OPn7wQ@mail.gmail.com/ Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20250708-discovery-pmt-fix-32-bit-formats-v1-1-296a5fc9c3d4@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/discovery-kunit.c | 2 +- drivers/platform/x86/intel/pmt/discovery.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/discovery-kunit.c b/drivers/platform/x86/intel/pmt/discovery-kunit.c index b4493fb96738a..f44eb41d58f64 100644 --- a/drivers/platform/x86/intel/pmt/discovery-kunit.c +++ b/drivers/platform/x86/intel/pmt/discovery-kunit.c @@ -32,7 +32,7 @@ validate_pmt_regions(struct kunit *test, struct pmt_feature_group *feature_group kunit_info(test, "\t\tbus=%u, device=%u, function=%u, guid=0x%x,", region->plat_info.bus_number, region->plat_info.device_number, region->plat_info.function_number, region->guid); - kunit_info(test, "\t\taddr=%p, size=%lu, num_rmids=%u", region->addr, region->size, + kunit_info(test, "\t\taddr=%p, size=%zu, num_rmids=%u", region->addr, region->size, region->num_rmids); diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index e72d43b675b46..1a680a042a98e 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -424,7 +424,7 @@ pmt_feature_get_feature_table(struct pmt_features_priv *priv, size = sizeof(*header) + FEAT_ATTR_SIZE(header->attr_size) + PMT_GUID_SIZE(header->num_guids); res_size = resource_size(&res); - if (WARN(size > res_size, "Bad table size %ld > %pa", size, &res_size)) + if (WARN(size > res_size, "Bad table size %zu > %pa", size, &res_size)) return -EINVAL; /* Get the feature attributes, including capability fields */ -- GitLab From a582469541a3f39bed452c50c5d2744620b6db02 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:43 +0300 Subject: [PATCH 511/868] pwm: img: Remove redundant pm_runtime_mark_last_busy() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://lore.kernel.org/r/20250704075443.3221370-1-sakari.ailus@linux.intel.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-img.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index 71542956feca9..91e0b19f0c08d 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -139,7 +139,6 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, (timebase << PWM_CH_CFG_TMBASE_SHIFT); img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val); - pm_runtime_mark_last_busy(pwmchip_parent(chip)); pm_runtime_put_autosuspend(pwmchip_parent(chip)); return 0; @@ -175,7 +174,6 @@ static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val &= ~BIT(pwm->hwpwm); img_pwm_writel(imgchip, PWM_CTRL_CFG, val); - pm_runtime_mark_last_busy(pwmchip_parent(chip)); pm_runtime_put_autosuspend(pwmchip_parent(chip)); } -- GitLab From bde430fb669d03a0025fd90485dc26acfafd9b4f Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Wed, 9 Jul 2025 10:54:13 +0000 Subject: [PATCH 512/868] platform/x86/amd/hsmp: Enhance the print messages to prevent confusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the HSMP ACPI device is present, loading the amd_hsmp.ko module incorrectly displays the message "HSMP is not supported on Family:%x model:%x\n" despite being supported by the hsmp_acpi.ko module, leading to confusion. To address this issue, relocate the acpi_dev_present() check to the beginning of the hsmp_plt_init() and revise the print message to better reflect the current support status. Additionally, add more error messages in the error paths and debug messages to indicate successful probing for both hsmp_acpi.ko and amd_hsmp.ko modules. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Suma Hegde Link: https://lore.kernel.org/r/20250709105413.2487851-1-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp/acpi.c | 9 +++++++-- drivers/platform/x86/amd/hsmp/plat.c | 28 ++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 2f1faa82d13e8..d974c2289f5a1 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -587,8 +587,10 @@ static int hsmp_acpi_probe(struct platform_device *pdev) if (!hsmp_pdev->is_probed) { hsmp_pdev->num_sockets = amd_num_nodes(); - if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) { + dev_err(&pdev->dev, "Wrong number of sockets\n"); return -ENODEV; + } hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets, sizeof(*hsmp_pdev->sock), @@ -605,9 +607,12 @@ static int hsmp_acpi_probe(struct platform_device *pdev) if (!hsmp_pdev->is_probed) { ret = hsmp_misc_register(&pdev->dev); - if (ret) + if (ret) { + dev_err(&pdev->dev, "Failed to register misc device\n"); return ret; + } hsmp_pdev->is_probed = true; + dev_dbg(&pdev->dev, "AMD HSMP ACPI is probed successfully\n"); } return 0; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index e3874c47ed9ed..f8aa844d33e41 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -215,7 +217,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) return ret; } - return hsmp_misc_register(&pdev->dev); + ret = hsmp_misc_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register misc device\n"); + return ret; + } + + dev_dbg(&pdev->dev, "AMD HSMP is probed successfully\n"); + return 0; } static void hsmp_pltdrv_remove(struct platform_device *pdev) @@ -287,15 +296,20 @@ static int __init hsmp_plt_init(void) { int ret = -ENODEV; + if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1)) { + if (IS_ENABLED(CONFIG_AMD_HSMP_ACPI)) + pr_debug("HSMP is supported through ACPI on this platform, please use hsmp_acpi.ko\n"); + else + pr_info("HSMP is supported through ACPI on this platform, please enable AMD_HSMP_ACPI config\n"); + return -ENODEV; + } + if (!legacy_hsmp_support()) { - pr_info("HSMP is not supported on Family:%x model:%x\n", + pr_info("HSMP interface is either disabled or not supported on family:%x model:%x\n", boot_cpu_data.x86, boot_cpu_data.x86_model); return ret; } - if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1)) - return -ENODEV; - hsmp_pdev = get_hsmp_pdev(); if (!hsmp_pdev) return -ENOMEM; @@ -305,8 +319,10 @@ static int __init hsmp_plt_init(void) * if we have N SMN/DF interfaces that ideally means N sockets */ hsmp_pdev->num_sockets = amd_num_nodes(); - if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES) { + pr_err("Wrong number of sockets\n"); return ret; + } ret = platform_driver_register(&amd_hsmp_driver); if (ret) -- GitLab From c61da55412a08268ea0cdef99dea11f7ade934ee Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 9 Jul 2025 10:57:45 +0800 Subject: [PATCH 513/868] ASoC: sdw_utils: Add missed component_name strings for speaker amps Several speaker amp was missed when the compoennt_name was added, which results missing " spk:" from the components string, confusing UCM. Fixes: f792733e08d5 ("ASoC: sdw_utils: add component_name string to dai_info") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250709025745.1285788-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sdw_utils/soc_sdw_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index a744ca019378a..1580331cd34c5 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -218,6 +218,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt1308-aif", + .component_name = "rt1308", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -238,6 +239,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "rt1316-aif", + .component_name = "rt1316", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -257,6 +259,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, true}, .dai_name = "rt1318-aif", + .component_name = "rt1318", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, .init = asoc_sdw_rt_amp_init, @@ -370,6 +373,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "rt721-sdca-aif2", + .component_name = "rt721", .dai_type = SOC_SDW_DAI_TYPE_AMP, /* No feedback capability is provided by rt721-sdca codec driver*/ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, @@ -596,6 +600,7 @@ struct asoc_sdw_codec_info codec_info_list[] = { { .direction = {true, false}, .dai_name = "cs42l43-dp6", + .component_name = "cs42l43", .dai_type = SOC_SDW_DAI_TYPE_AMP, .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs42l43_spk_init, -- GitLab From 907e01b3ce4cee04aed33cdffcd444d6bd2a684e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 9 Jul 2025 14:46:52 +0200 Subject: [PATCH 514/868] ALSA: echoaudio: Replace deprecated strcpy() with strscpy() strcpy() is deprecated; use strscpy() instead. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20250709124655.1195-1-thorsten.blum@linux.dev Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 80d8ce75fdbb3..2b33ef588ac31 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -6,6 +6,7 @@ */ #include +#include MODULE_AUTHOR("Giuliano Pochini "); MODULE_LICENSE("GPL v2"); @@ -916,7 +917,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -929,7 +930,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); #endif /* ECHOCARD_HAS_DIGITAL_IO */ @@ -949,7 +950,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->analog_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -963,7 +964,7 @@ static int snd_echo_new_pcm(struct echoaudio *chip) return err; pcm->private_data = chip; chip->digital_pcm = pcm; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); snd_echo_preallocate_pages(pcm, &chip->pci->dev); @@ -1985,8 +1986,8 @@ static int __snd_echo_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "Echo_" ECHOCARD_NAME); - strcpy(card->shortname, chip->card_name); + strscpy(card->driver, "Echo_" ECHOCARD_NAME); + strscpy(card->shortname, chip->card_name); dsp = "56301"; if (pci_id->device == 0x3410) -- GitLab From 0cf6d425d39cfc1b676fbf9dea36ecd68eeb27ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 30 Jun 2025 15:03:57 +0200 Subject: [PATCH 515/868] gpio: sim: allow to mark simulated lines as invalid Add a new line-level, boolean property to the gpio-sim configfs interface called 'valid'. It's set by default and the user can unset it to make the line be included in the standard `gpio-reserved-ranges` property when the chip is registered with GPIO core. This allows users to specify which lines should not be available for requesting as GPIOs. Link: https://lore.kernel.org/r/20250630130358.40352-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- Documentation/admin-guide/gpio/gpio-sim.rst | 7 +- drivers/gpio/gpio-sim.c | 83 ++++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/gpio/gpio-sim.rst b/Documentation/admin-guide/gpio/gpio-sim.rst index 35d49ccd49e0a..f5135a14ef2e0 100644 --- a/Documentation/admin-guide/gpio/gpio-sim.rst +++ b/Documentation/admin-guide/gpio/gpio-sim.rst @@ -50,8 +50,11 @@ the number of lines exposed by this bank. **Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/name`` -This group represents a single line at the offset Y. The 'name' attribute -allows to set the line name as represented by the 'gpio-line-names' property. +**Attribute:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/valid`` + +This group represents a single line at the offset Y. The ``valid`` attribute +indicates whether the line can be used as GPIO. The ``name`` attribute allows +to set the line name as represented by the 'gpio-line-names' property. **Item:** ``/config/gpio-sim/gpio-device/gpio-bankX/lineY/hog`` diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index f638219a7c4f0..9503296422fd2 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -39,7 +39,7 @@ #include "dev-sync-probe.h" #define GPIO_SIM_NGPIO_MAX 1024 -#define GPIO_SIM_PROP_MAX 4 /* Max 3 properties + sentinel. */ +#define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */ #define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */ static DEFINE_IDA(gpio_sim_ida); @@ -629,6 +629,7 @@ struct gpio_sim_line { unsigned int offset; char *name; + bool valid; /* There can only be one hog per line. */ struct gpio_sim_hog *hog; @@ -744,6 +745,36 @@ gpio_sim_set_line_names(struct gpio_sim_bank *bank, char **line_names) } } +static unsigned int gpio_sim_get_reserved_ranges_size(struct gpio_sim_bank *bank) +{ + struct gpio_sim_line *line; + unsigned int size = 0; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->valid) + continue; + + size += 2; + } + + return size; +} + +static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank, + u32 *ranges) +{ + struct gpio_sim_line *line; + int i = 0; + + list_for_each_entry(line, &bank->line_list, siblings) { + if (line->valid) + continue; + + ranges[i++] = line->offset; + ranges[i++] = 1; + } +} + static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) { struct gpiod_hog *hog; @@ -844,9 +875,10 @@ static struct fwnode_handle * gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, struct fwnode_handle *parent) { + unsigned int prop_idx = 0, line_names_size, ranges_size; struct property_entry properties[GPIO_SIM_PROP_MAX]; - unsigned int prop_idx = 0, line_names_size; char **line_names __free(kfree) = NULL; + u32 *ranges __free(kfree) = NULL; memset(properties, 0, sizeof(properties)); @@ -870,6 +902,19 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, line_names, line_names_size); } + ranges_size = gpio_sim_get_reserved_ranges_size(bank); + if (ranges_size) { + ranges = kcalloc(ranges_size, sizeof(u32), GFP_KERNEL); + if (!ranges) + return ERR_PTR(-ENOMEM); + + gpio_sim_set_reserved_ranges(bank, ranges); + + properties[prop_idx++] = PROPERTY_ENTRY_U32_ARRAY_LEN( + "gpio-reserved-ranges", + ranges, ranges_size); + } + return fwnode_create_software_node(properties, parent); } @@ -1189,8 +1234,41 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item, CONFIGFS_ATTR(gpio_sim_line_config_, name); +static ssize_t +gpio_sim_line_config_valid_show(struct config_item *item, char *page) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + + guard(mutex)(&dev->lock); + + return sprintf(page, "%c\n", line->valid ? '1' : '0'); +} + +static ssize_t gpio_sim_line_config_valid_store(struct config_item *item, + const char *page, size_t count) +{ + struct gpio_sim_line *line = to_gpio_sim_line(item); + struct gpio_sim_device *dev = gpio_sim_line_get_device(line); + bool valid; + int ret; + + ret = kstrtobool(page, &valid); + if (ret) + return ret; + + guard(mutex)(&dev->lock); + + line->valid = valid; + + return count; +} + +CONFIGFS_ATTR(gpio_sim_line_config_, valid); + static struct configfs_attribute *gpio_sim_line_config_attrs[] = { &gpio_sim_line_config_attr_name, + &gpio_sim_line_config_attr_valid, NULL }; @@ -1399,6 +1477,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, line->parent = bank; line->offset = offset; + line->valid = true; list_add_tail(&line->siblings, &bank->line_list); return &line->group; -- GitLab From ad4655653a6c463026ed3c300e5fb34f39abff48 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Jul 2025 17:24:17 +0200 Subject: [PATCH 516/868] ASoC: SDCA: fix HID dependency It is not possible to enable SND_SOC_SDCA_HID when SND_SOC_SDCA is built-in but HID is in a loadable module, as that results in a link failure: x86_64-linux-ld: sound/soc/sdca/sdca_functions.o: in function `find_sdca_entity_hide': sdca_functions.c:(.text+0x25b): undefined reference to `sdca_add_hid_device' Change SND_SOC_SDCA_HID into a 'bool' option that can only be enabled if this results in a working build, and change the Makefile so this driver is a loadable module if possible. Fixes: ac558015dfd8 ("ASoC: SDCA: add a HID device for HIDE entity") Reviewed-by: Charles Keepax Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20250709152430.1498427-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/sdca/Kconfig | 5 +++-- sound/soc/sdca/Makefile | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index 53f6926255ae3..2253a300dcc35 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -12,8 +12,9 @@ config SND_SOC_SDCA_OPTIONAL def_tristate SND_SOC_SDCA || !SND_SOC_SDCA config SND_SOC_SDCA_HID - tristate "SDCA HID support" - depends on SND_SOC_SDCA && HID + bool "SDCA HID support" + depends on SND_SOC_SDCA + depends on HID=y || HID=SND_SOC_SDCA config SND_SOC_SDCA_IRQ tristate diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 2a3938d11ca90..1efc869c6cbc3 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -5,5 +5,7 @@ snd-soc-sdca-hid-y := sdca_hid.o snd-soc-sdca-irq-y := sdca_interrupts.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o -obj-$(CONFIG_SND_SOC_SDCA_HID) += snd-soc-sdca-hid.o +ifdef CONFIG_SND_SOC_SDCA_HID +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-hid.o +endif obj-$(CONFIG_SND_SOC_SDCA_IRQ) += snd-soc-sdca-irq.o -- GitLab From 469d7ea8e99124e4def5d98f928ffb4a659aa1c8 Mon Sep 17 00:00:00 2001 From: Darshan Rathod Date: Thu, 10 Jul 2025 04:50:57 +0000 Subject: [PATCH 517/868] spi: xilinx: Fix block comment style and minor cleanups This patch fixes block comment style issues and minor code cleanups as reported by checkpatch.pl. Signed-off-by: Darshan Rathod Link: https://patch.msgid.link/20250710045058.1325-1-darshanrathod475@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index ded709b2b4591..d59cc8a184846 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -89,8 +89,8 @@ struct xilinx_spi { u8 bytes_per_word; int buffer_size; /* buffer size in words */ u32 cs_inactive; /* Level of the CS pins when inactive*/ - unsigned int (*read_fn)(void __iomem *); - void (*write_fn)(u32, void __iomem *); + unsigned int (*read_fn)(void __iomem *addr); + void (*write_fn)(u32 val, void __iomem *addr); }; static void xspi_write32(u32 val, void __iomem *addr) @@ -251,6 +251,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) if (xspi->irq >= 0 && (xspi->force_irq || remaining_words > xspi->buffer_size)) { u32 isr; + use_irq = true; /* Inhibit irq to avoid spurious irqs on tx_empty*/ cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); -- GitLab From 3106db4ead939a70d332a66214eccce6805b991f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2025 21:02:33 +0200 Subject: [PATCH 518/868] spi: sh-msiof: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Convert the Renesas SuperH MSIOF driver from SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This lets us drop the check for CONFIG_PM_SLEEP without impacting code size, while increasing build coverage. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/108c136f2cab9aa8bc8ac90d14a05e66fb87deb0.1752087740.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 94a867967e024..b695870fae8c4 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1320,7 +1320,6 @@ static const struct platform_device_id spi_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); -#ifdef CONFIG_PM_SLEEP static int sh_msiof_spi_suspend(struct device *dev) { struct sh_msiof_spi_priv *p = dev_get_drvdata(dev); @@ -1335,12 +1334,8 @@ static int sh_msiof_spi_resume(struct device *dev) return spi_controller_resume(p->ctlr); } -static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, - sh_msiof_spi_resume); -#define DEV_PM_OPS (&sh_msiof_spi_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, + sh_msiof_spi_resume); static struct platform_driver sh_msiof_spi_drv = { .probe = sh_msiof_spi_probe, @@ -1348,7 +1343,7 @@ static struct platform_driver sh_msiof_spi_drv = { .id_table = spi_driver_ids, .driver = { .name = "spi_sh_msiof", - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&sh_msiof_spi_pm_ops), .of_match_table = of_match_ptr(sh_msiof_match), }, }; -- GitLab From 7d61715c58a39edc5f74fc7366487726fc223530 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2025 21:02:09 +0200 Subject: [PATCH 519/868] spi: rspi: Convert to DEFINE_SIMPLE_DEV_PM_OPS() Convert the Renesas RSPI/QSPI driver from SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This lets us drop the check for CONFIG_PM_SLEEP without impacting code size, while increasing build coverage. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/0b64c1c3803e6d3eeb3ae9cd8921d4fe67f37118.1752087701.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 92faaf614f8ea..8e1d911b88b51 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1404,7 +1404,6 @@ static const struct platform_device_id spi_driver_ids[] = { MODULE_DEVICE_TABLE(platform, spi_driver_ids); -#ifdef CONFIG_PM_SLEEP static int rspi_suspend(struct device *dev) { struct rspi_data *rspi = dev_get_drvdata(dev); @@ -1419,11 +1418,7 @@ static int rspi_resume(struct device *dev) return spi_controller_resume(rspi->ctlr); } -static SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume); -#define DEV_PM_OPS &rspi_pm_ops -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ +static DEFINE_SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume); static struct platform_driver rspi_driver = { .probe = rspi_probe, @@ -1431,7 +1426,7 @@ static struct platform_driver rspi_driver = { .id_table = spi_driver_ids, .driver = { .name = "renesas_spi", - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&rspi_pm_ops), .of_match_table = of_match_ptr(rspi_of_match), }, }; -- GitLab From 626bb0a45584d544d84eab909795ccb355062bcc Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:12 +0200 Subject: [PATCH 520/868] mfd: tps6594: Add TI TPS652G1 support The TPS652G1 is a stripped down version of the TPS65224. From a software point of view, it lacks any voltage monitoring, the watchdog, the ESM and the ADC. Signed-off-by: Michael Walle Link: https://lore.kernel.org/r/20250613114518.1772109-2-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/mfd/tps6594-core.c | 88 ++++++++++++++++++++++++++++++++++--- drivers/mfd/tps6594-i2c.c | 10 ++++- drivers/mfd/tps6594-spi.c | 10 ++++- include/linux/mfd/tps6594.h | 1 + 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c index a7223e873cd16..c16c37e366178 100644 --- a/drivers/mfd/tps6594-core.c +++ b/drivers/mfd/tps6594-core.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Core functions for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * Core functions for following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -414,6 +419,61 @@ static const unsigned int tps65224_irq_reg[] = { TPS6594_REG_INT_FSM_ERR, }; +/* TPS652G1 Resources */ + +static const struct mfd_cell tps652g1_common_cells[] = { + MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources), + MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources), + MFD_CELL_NAME("tps6594-regulator"), +}; + +static const struct regmap_irq tps652g1_irqs[] = { + /* INT_GPIO register */ + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT), + + /* INT_STARTUP register */ + REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT), + + /* INT_MISC register */ + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT), + + /* INT_MODERATE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT), + + /* INT_SEVERE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT), + + /* INT_FSM_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT), +}; + static inline unsigned int tps6594_get_irq_reg(struct regmap_irq_chip_data *data, unsigned int base, int index) { @@ -443,7 +503,7 @@ static int tps6594_handle_post_irq(void *irq_drv_data) * a new interrupt. */ if (tps->use_crc) { - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_INT_FSM_ERR; mask_val = TPS6594_BIT_COMM_ERR_INT; } else { @@ -481,6 +541,18 @@ static struct regmap_irq_chip tps65224_irq_chip = { .handle_post_irq = tps6594_handle_post_irq, }; +static struct regmap_irq_chip tps652g1_irq_chip = { + .ack_base = TPS6594_REG_INT_BUCK, + .ack_invert = 1, + .clear_ack = 1, + .init_ack_masked = 1, + .num_regs = ARRAY_SIZE(tps65224_irq_reg), + .irqs = tps652g1_irqs, + .num_irqs = ARRAY_SIZE(tps652g1_irqs), + .get_irq_reg = tps65224_get_irq_reg, + .handle_post_irq = tps6594_handle_post_irq, +}; + static const struct regmap_range tps6594_volatile_ranges[] = { regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_READBACK_ERR), regmap_reg_range(TPS6594_REG_RTC_STATUS, TPS6594_REG_RTC_STATUS), @@ -507,7 +579,7 @@ static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -537,7 +609,7 @@ static int tps6594_set_crc_feature(struct tps6594 *tps) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -628,6 +700,10 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) irq_chip = &tps65224_irq_chip; n_cells = ARRAY_SIZE(tps65224_common_cells); cells = tps65224_common_cells; + } else if (tps->chip_id == TPS652G1) { + irq_chip = &tps652g1_irq_chip; + n_cells = ARRAY_SIZE(tps652g1_common_cells); + cells = tps652g1_common_cells; } else { irq_chip = &tps6594_irq_chip; n_cells = ARRAY_SIZE(tps6594_common_cells); @@ -651,8 +727,8 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) if (ret) return dev_err_probe(dev, ret, "Failed to add common child devices\n"); - /* No RTC for LP8764 and TPS65224 */ - if (tps->chip_id != LP8764 && tps->chip_id != TPS65224) { + /* No RTC for LP8764, TPS65224 and TPS652G1 */ + if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) { ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells, ARRAY_SIZE(tps6594_rtc_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); diff --git a/drivers/mfd/tps6594-i2c.c b/drivers/mfd/tps6594-i2c.c index 4ab91c34d9fb9..7ff7516286fd9 100644 --- a/drivers/mfd/tps6594-i2c.c +++ b/drivers/mfd/tps6594-i2c.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * I2C access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * I2C access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -197,6 +202,7 @@ static const struct of_device_id tps6594_i2c_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table); @@ -222,7 +228,7 @@ static int tps6594_i2c_probe(struct i2c_client *client) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_i2c_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config); diff --git a/drivers/mfd/tps6594-spi.c b/drivers/mfd/tps6594-spi.c index 6ebccb79f0cc8..944b7313a1d9b 100644 --- a/drivers/mfd/tps6594-spi.c +++ b/drivers/mfd/tps6594-spi.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * SPI access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * SPI access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -82,6 +87,7 @@ static const struct of_device_id tps6594_spi_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table); @@ -107,7 +113,7 @@ static int tps6594_spi_probe(struct spi_device *spi) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_spi_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config); diff --git a/include/linux/mfd/tps6594.h b/include/linux/mfd/tps6594.h index 16543fd4d83e2..021db8875963a 100644 --- a/include/linux/mfd/tps6594.h +++ b/include/linux/mfd/tps6594.h @@ -19,6 +19,7 @@ enum pmic_id { TPS6593, LP8764, TPS65224, + TPS652G1, }; /* Macro to get page index from register address */ -- GitLab From 9cba6a7ebf65c603b80c0b3c7fa8c7c03f1b704c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:13 +0200 Subject: [PATCH 521/868] misc: tps6594-pfsm: Add TI TPS652G1 PMIC PFSM The TPS652G1 is a stripped down TPS65224, but the PFSM is the same. Thus, handle it the same way as the TPS65224 in the driver. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann # drivers/misc/ Link: https://lore.kernel.org/r/20250613114518.1772109-3-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/misc/tps6594-pfsm.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 6db1c9d48f8fc..44fa81d6cec23 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * PFSM (Pre-configurable Finite State Machine) driver for the following + * PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6594 + * - TPS6593 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -141,7 +147,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a switch (cmd) { case PMIC_GOTO_STANDBY: /* Disable LP mode on TPS6594 Family PMIC */ - if (pfsm->chip_id != TPS65224) { + if (pfsm->chip_id != TPS65224 && pfsm->chip_id != TPS652G1) { ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2, TPS6594_BIT_LP_STANDBY_SEL); @@ -154,8 +160,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0)); break; case PMIC_GOTO_LP_STANDBY: - /* TPS65224 does not support LP STANDBY */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support LP STANDBY */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; /* Enable LP mode */ @@ -179,8 +185,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B); break; case PMIC_SET_MCU_ONLY_STATE: - /* TPS65224 does not support MCU_ONLY_STATE */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support MCU_ONLY_STATE */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; if (copy_from_user(&state_opt, argp, sizeof(state_opt))) @@ -206,7 +212,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return -EFAULT; /* Configure wake-up destination */ - if (pfsm->chip_id == TPS65224) { + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) { regmap_reg = TPS65224_REG_STARTUP_CTRL; mask = TPS65224_MASK_STARTUP_DEST; } else { @@ -230,9 +236,14 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return ret; /* Modify NSLEEP1-2 bits */ - ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS, - pfsm->chip_id == TPS65224 ? - TPS6594_BIT_NSLEEP1B : TPS6594_BIT_NSLEEP2B); + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP1B); + else + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP2B); break; } -- GitLab From f6420de1c810e282c34de65c70e6cc6177c12394 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:14 +0200 Subject: [PATCH 522/868] pinctrl: pinctrl-tps6594: Add TPS652G1 PMIC pinctrl and GPIO The TPS652G1 is a stripped down version of the TPS65224. Compared to the TPS65224 it lacks some pin mux functions, like the ADC, voltage monitoring and the second I2C bus. Signed-off-by: Michael Walle Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250613114518.1772109-4-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/pinctrl/pinctrl-tps6594.c | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c index 54cc810f79d60..6726853110d10 100644 --- a/drivers/pinctrl/pinctrl-tps6594.c +++ b/drivers/pinctrl/pinctrl-tps6594.c @@ -226,6 +226,10 @@ static const char *const tps65224_nerr_mcu_func_group_names[] = { "GPIO5", }; +static const char *const tps652g1_cs_spi_func_group_names[] = { + "GPIO1", +}; + struct tps6594_pinctrl_function { struct pinfunction pinfunction; u8 muxval; @@ -287,6 +291,18 @@ static const struct tps6594_pinctrl_function tps65224_pinctrl_functions[] = { FUNCTION(tps65224, nerr_mcu, TPS65224_PINCTRL_NERR_MCU_FUNCTION), }; +static const struct tps6594_pinctrl_function tps652g1_pinctrl_functions[] = { + FUNCTION(tps65224, gpio, TPS6594_PINCTRL_GPIO_FUNCTION), + FUNCTION(tps65224, sda_i2c2_sdo_spi, TPS65224_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION), + FUNCTION(tps65224, nsleep2, TPS65224_PINCTRL_NSLEEP2_FUNCTION), + FUNCTION(tps65224, nint, TPS65224_PINCTRL_NINT_FUNCTION), + FUNCTION(tps652g1, cs_spi, TPS65224_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION), + FUNCTION(tps65224, nsleep1, TPS65224_PINCTRL_NSLEEP1_FUNCTION), + FUNCTION(tps65224, pb, TPS65224_PINCTRL_PB_FUNCTION), + FUNCTION(tps65224, wkup, TPS65224_PINCTRL_WKUP_FUNCTION), + FUNCTION(tps65224, syncclkin, TPS65224_PINCTRL_SYNCCLKIN_FUNCTION), +}; + struct tps6594_pinctrl { struct tps6594 *tps; struct gpio_regmap *gpio_regmap; @@ -300,6 +316,16 @@ struct tps6594_pinctrl { struct muxval_remap *remap; }; +static struct tps6594_pinctrl tps652g1_template_pinctrl = { + .funcs = tps652g1_pinctrl_functions, + .func_cnt = ARRAY_SIZE(tps652g1_pinctrl_functions), + .pins = tps65224_pins, + .num_pins = ARRAY_SIZE(tps65224_pins), + .mux_sel_mask = TPS65224_MASK_GPIO_SEL, + .remap = tps65224_muxval_remap, + .remap_cnt = ARRAY_SIZE(tps65224_muxval_remap), +}; + static struct tps6594_pinctrl tps65224_template_pinctrl = { .funcs = tps65224_pinctrl_functions, .func_cnt = ARRAY_SIZE(tps65224_pinctrl_functions), @@ -475,6 +501,15 @@ static int tps6594_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; switch (tps->chip_id) { + case TPS652G1: + pctrl_desc->pins = tps65224_pins; + pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); + + *pinctrl = tps652g1_template_pinctrl; + + config.ngpio = ARRAY_SIZE(tps65224_gpio_func_group_names); + config.ngpio_per_reg = TPS65224_NGPIO_PER_REG; + break; case TPS65224: pctrl_desc->pins = tps65224_pins; pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); -- GitLab From d90171bc2e5f69c038d1807e6f64fba3d1ad6bee Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:46 +0200 Subject: [PATCH 523/868] dt-bindings: mfd: ti,tps6594: Add TI TPS652G1 PMIC The TPS652G1 is a stripped down version of the TPS65224. From a software point of view, it lacks any voltage monitoring, the watchdog, the ESM and the ADC. Signed-off-by: Michael Walle Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250703113153.2447110-2-mwalle@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/ti,tps6594.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml index 6341b6070366e..a48cb00afe438 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml @@ -22,6 +22,7 @@ properties: - ti,tps6593-q1 - ti,tps6594-q1 - ti,tps65224-q1 + - ti,tps652g1 reg: description: I2C slave address or SPI chip select number. -- GitLab From 16d1a9bf36ef649b1fdb866985b4b87584491fac Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:50 +0200 Subject: [PATCH 524/868] regulator: tps6594-regulator: remove interrupt_count In .probe() interrupt_count and nr_types is essentially the same. It contains the number of different interrupt per LDO or buck converter. Drop one. This is a preparation patch to further simplify the handling of different variants of this PMIC. This patch is only compile-time tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-6-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 51264c869aa02..26669f3f10333 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -577,18 +577,15 @@ static int tps6594_regulator_probe(struct platform_device *pdev) const struct regulator_desc *multi_regs; const struct tps6594_regulator_irq_type **ldos_irq_types; const struct regulator_desc *ldo_regs; - size_t interrupt_count; if (tps->chip_id == TPS65224) { bucks_irq_types = tps65224_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps65224_buck1_irq_types); multi_regs = tps65224_multi_regs; ldos_irq_types = tps65224_ldos_irq_types; ldo_regs = tps65224_ldo_regs; multi_phase_cnt = ARRAY_SIZE(tps65224_multi_regs); } else { bucks_irq_types = tps6594_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps6594_buck1_irq_types); multi_regs = tps6594_multi_regs; ldos_irq_types = tps6594_ldos_irq_types; ldo_regs = tps6594_ldo_regs; @@ -686,29 +683,27 @@ static int tps6594_regulator_probe(struct platform_device *pdev) error = tps6594_request_reg_irqs(pdev, rdev, irq_data, bucks_irq_types[buck_idx], - interrupt_count, &irq_idx); + nr_types, &irq_idx); if (error) return error; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, bucks_irq_types[buck_idx + 1], - interrupt_count, &irq_idx); + nr_types, &irq_idx); if (error) return error; if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, tps6594_bucks_irq_types[buck_idx + 2], - interrupt_count, - &irq_idx); + nr_types, &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, tps6594_bucks_irq_types[buck_idx + 3], - interrupt_count, - &irq_idx); + nr_types, &irq_idx); if (error) return error; } @@ -727,7 +722,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[i], interrupt_count, &irq_idx); + bucks_irq_types[i], nr_types, &irq_idx); if (error) return error; } @@ -742,7 +737,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) pdev->name); error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - ldos_irq_types[i], interrupt_count, + ldos_irq_types[i], nr_types, &irq_idx); if (error) return error; -- GitLab From 180a135eafa9e05657559bb04cc9eb6a86ca45f3 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:51 +0200 Subject: [PATCH 525/868] regulator: tps6594-regulator: remove hardcoded buck config Commit 00c826525fba ("regulator: tps6594-regulator: Add TI TPS65224 PMIC regulators") added support for the TPS65224 and made the description of the multi-phase buck converter variable depending on the variant of the PMIC. But this was just done for MUTLI_BUCK12 and MULTI_BUCK12_34 configs probably because this variant only supports a multi-phase configuration on buck 1 and 2. Remove the hardcoded value for the remaining two configs, too as future PMIC variants might also support these. This is a preparation patch to refactor the regulator description and is compile-time only tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-7-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 26669f3f10333..2c7c4df80695e 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -695,14 +695,14 @@ static int tps6594_regulator_probe(struct platform_device *pdev) if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 2], + bucks_irq_types[buck_idx + 2], nr_types, &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 3], + bucks_irq_types[buck_idx + 3], nr_types, &irq_idx); if (error) return error; -- GitLab From e64ee27abfe1e9baea14b31c0a6b6bf93ac8652c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:52 +0200 Subject: [PATCH 526/868] regulator: tps6594-regulator: refactor variant descriptions Instead of using conditionals or tri state operators throughout the .probe() provide a description per variant. This will make it much easier to add new variants later. While at it, make the variable naming more consistent. This patch is only compile-time tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-8-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 199 +++++++++++++++----------- 1 file changed, 112 insertions(+), 87 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 2c7c4df80695e..39adb2db6de80 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -21,10 +21,6 @@ #define BUCK_NB 5 #define LDO_NB 4 #define MULTI_PHASE_NB 4 -/* TPS6593 and LP8764 supports OV, UV, SC, ILIM */ -#define REGS_INT_NB 4 -/* TPS65224 supports OV or UV */ -#define TPS65224_REGS_INT_NB 1 enum tps6594_regulator_id { /* DCDC's */ @@ -192,7 +188,7 @@ static const struct regulator_ops tps6594_ldos_4_ops = { .map_voltage = regulator_map_voltage_linear_range, }; -static const struct regulator_desc buck_regs[] = { +static const struct regulator_desc tps6594_buck_regs[] = { TPS6594_REGULATOR("BUCK1", "buck1", TPS6594_BUCK_1, REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS6594_MASK_BUCKS_VSET, TPS6594_REG_BUCKX_VOUT_1(0), @@ -549,6 +545,63 @@ static int tps6594_request_reg_irqs(struct platform_device *pdev, return 0; } +struct tps6594_regulator_desc { + const struct regulator_desc *multi_phase_regs; + unsigned int num_multi_phase_regs; + + const struct regulator_desc *buck_regs; + int num_buck_regs; + + const struct regulator_desc *ldo_regs; + int num_ldo_regs; + + const struct tps6594_regulator_irq_type **bucks_irq_types; + const struct tps6594_regulator_irq_type **ldos_irq_types; + int num_irq_types; + + const struct tps6594_regulator_irq_type *ext_irq_types; + int num_ext_irqs; +}; + +static const struct tps6594_regulator_desc tps65224_reg_desc = { + .multi_phase_regs = tps65224_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps65224_multi_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .bucks_irq_types = tps65224_bucks_irq_types, + .ldos_irq_types = tps65224_ldos_irq_types, + .num_irq_types = 1, /* OV or UV */ + .ext_irq_types = tps65224_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types), +}; + +static const struct tps6594_regulator_desc tps6594_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .ldo_regs = tps6594_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps6594_ldo_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .ldos_irq_types = tps6594_ldos_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = 2, /* only VCCA OV and UV */ +}; + +static const struct tps6594_regulator_desc lp8764_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps6594_ext_regulator_irq_types), +}; + static int tps6594_regulator_probe(struct platform_device *pdev) { struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); @@ -559,38 +612,32 @@ static int tps6594_regulator_probe(struct platform_device *pdev) struct tps6594_regulator_irq_data *irq_data; struct tps6594_ext_regulator_irq_data *irq_ext_reg_data; const struct tps6594_regulator_irq_type *irq_type; - const struct tps6594_regulator_irq_type *irq_types; bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; + const struct tps6594_regulator_desc *desc; + const struct regulator_desc *multi_regs; const char *npname; int error, i, irq, multi; int irq_idx = 0; int buck_idx = 0; - int nr_ldo; - int nr_buck; - int nr_types; - unsigned int irq_count; - unsigned int multi_phase_cnt; size_t reg_irq_nb; - const struct tps6594_regulator_irq_type **bucks_irq_types; - const struct regulator_desc *multi_regs; - const struct tps6594_regulator_irq_type **ldos_irq_types; - const struct regulator_desc *ldo_regs; - if (tps->chip_id == TPS65224) { - bucks_irq_types = tps65224_bucks_irq_types; - multi_regs = tps65224_multi_regs; - ldos_irq_types = tps65224_ldos_irq_types; - ldo_regs = tps65224_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps65224_multi_regs); - } else { - bucks_irq_types = tps6594_bucks_irq_types; - multi_regs = tps6594_multi_regs; - ldos_irq_types = tps6594_ldos_irq_types; - ldo_regs = tps6594_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps6594_multi_regs); - } + switch (tps->chip_id) { + case TPS65224: + desc = &tps65224_reg_desc; + break; + case TPS6594: + case TPS6593: + desc = &tps6594_reg_desc; + break; + case LP8764: + desc = &lp8764_reg_desc; + break; + default: + dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id); + return -EINVAL; + }; enum { MULTI_BUCK12, @@ -611,13 +658,14 @@ static int tps6594_regulator_probe(struct platform_device *pdev) * In case of Multiphase configuration, value should be defined for * buck_configured to avoid creating bucks for every buck in multiphase */ - for (multi = 0; multi < multi_phase_cnt; multi++) { - np = of_find_node_by_name(tps->dev->of_node, multi_regs[multi].supply_name); + for (multi = 0; multi < desc->num_multi_phase_regs; multi++) { + multi_regs = &desc->multi_phase_regs[multi]; + np = of_find_node_by_name(tps->dev->of_node, multi_regs->supply_name); npname = of_node_full_name(np); np_pmic_parent = of_get_parent(of_get_parent(np)); if (of_node_cmp(of_node_full_name(np_pmic_parent), tps->dev->of_node->full_name)) continue; - if (strcmp(npname, multi_regs[multi].supply_name) == 0) { + if (strcmp(npname, multi_regs->supply_name) == 0) { switch (multi) { case MULTI_BUCK12: buck_multi[0] = true; @@ -650,28 +698,19 @@ static int tps6594_regulator_probe(struct platform_device *pdev) } } - if (tps->chip_id == TPS65224) { - nr_buck = ARRAY_SIZE(tps65224_buck_regs); - nr_ldo = ARRAY_SIZE(tps65224_ldo_regs); - nr_types = TPS65224_REGS_INT_NB; - } else { - nr_buck = ARRAY_SIZE(buck_regs); - nr_ldo = (tps->chip_id == LP8764) ? 0 : ARRAY_SIZE(tps6594_ldo_regs); - nr_types = REGS_INT_NB; - } - - reg_irq_nb = nr_types * (nr_buck + nr_ldo); + reg_irq_nb = desc->num_irq_types * (desc->num_buck_regs + desc->num_ldo_regs); irq_data = devm_kmalloc_array(tps->dev, reg_irq_nb, sizeof(struct tps6594_regulator_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; - for (i = 0; i < multi_phase_cnt; i++) { + for (i = 0; i < desc->num_multi_phase_regs; i++) { if (!buck_multi[i]) continue; - rdev = devm_regulator_register(&pdev->dev, &multi_regs[i], &config); + rdev = devm_regulator_register(&pdev->dev, &desc->multi_phase_regs[i], + &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", @@ -682,89 +721,74 @@ static int tps6594_regulator_probe(struct platform_device *pdev) buck_idx = 2; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx], + desc->num_irq_types, &irq_idx); if (error) return error; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 1], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx + 1], + desc->num_irq_types, &irq_idx); if (error) return error; if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 2], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx + 2], + desc->num_irq_types, + &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 3], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx + 3], + desc->num_irq_types, + &irq_idx); if (error) return error; } } - for (i = 0; i < nr_buck; i++) { + for (i = 0; i < desc->num_buck_regs; i++) { if (buck_configured[i]) continue; - const struct regulator_desc *buck_cfg = (tps->chip_id == TPS65224) ? - tps65224_buck_regs : buck_regs; - - rdev = devm_regulator_register(&pdev->dev, &buck_cfg[i], &config); + rdev = devm_regulator_register(&pdev->dev, &desc->buck_regs[i], &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", pdev->name); error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[i], nr_types, &irq_idx); + desc->bucks_irq_types[i], + desc->num_irq_types, &irq_idx); if (error) return error; } - /* LP8764 doesn't have LDO */ - if (tps->chip_id != LP8764) { - for (i = 0; i < nr_ldo; i++) { - rdev = devm_regulator_register(&pdev->dev, &ldo_regs[i], &config); - if (IS_ERR(rdev)) - return dev_err_probe(tps->dev, PTR_ERR(rdev), - "failed to register %s regulator\n", - pdev->name); - - error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - ldos_irq_types[i], nr_types, - &irq_idx); - if (error) - return error; - } - } + for (i = 0; i < desc->num_ldo_regs; i++) { + rdev = devm_regulator_register(&pdev->dev, &desc->ldo_regs[i], &config); + if (IS_ERR(rdev)) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "failed to register %s regulator\n", + pdev->name); - if (tps->chip_id == TPS65224) { - irq_types = tps65224_ext_regulator_irq_types; - irq_count = ARRAY_SIZE(tps65224_ext_regulator_irq_types); - } else { - irq_types = tps6594_ext_regulator_irq_types; - if (tps->chip_id == LP8764) - irq_count = ARRAY_SIZE(tps6594_ext_regulator_irq_types); - else - /* TPS6593 supports only VCCA OV and UV */ - irq_count = 2; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, + desc->ldos_irq_types[i], + desc->num_irq_types, &irq_idx); + if (error) + return error; } irq_ext_reg_data = devm_kmalloc_array(tps->dev, - irq_count, + desc->num_ext_irqs, sizeof(struct tps6594_ext_regulator_irq_data), GFP_KERNEL); if (!irq_ext_reg_data) return -ENOMEM; - for (i = 0; i < irq_count; ++i) { - irq_type = &irq_types[i]; + for (i = 0; i < desc->num_ext_irqs; ++i) { + irq_type = &desc->ext_irq_types[i]; irq = platform_get_irq_byname(pdev, irq_type->irq_name); if (irq < 0) return -EINVAL; @@ -782,6 +806,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to request %s IRQ %d\n", irq_type->irq_name, irq); } + return 0; } -- GitLab From b30d390812c8559c5835f8ae5f490b38488fafc8 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:53 +0200 Subject: [PATCH 527/868] regulator: tps6594-regulator: Add TI TPS652G1 PMIC regulators The TI TPS652G1 is a stripped down version of the TPS65224 PMIC. It doesn't feature the multiphase buck converter nor any voltage monitoring. Due to the latter there are no interrupts serviced. In case of the TPS652G1 any interrupt related setup is just skipped. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-9-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 39adb2db6de80..ab882daec7c5f 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -577,6 +577,13 @@ static const struct tps6594_regulator_desc tps65224_reg_desc = { .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types), }; +static const struct tps6594_regulator_desc tps652g1_reg_desc = { + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), +}; + static const struct tps6594_regulator_desc tps6594_reg_desc = { .multi_phase_regs = tps6594_multi_regs, .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), @@ -627,6 +634,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) case TPS65224: desc = &tps65224_reg_desc; break; + case TPS652G1: + desc = &tps652g1_reg_desc; + break; case TPS6594: case TPS6593: desc = &tps6594_reg_desc; @@ -716,6 +726,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + /* config multiphase buck12+buck34 */ if (i == MULTI_BUCK12_34) buck_idx = 2; @@ -759,6 +772,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, desc->bucks_irq_types[i], desc->num_irq_types, &irq_idx); @@ -773,6 +789,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, desc->ldos_irq_types[i], desc->num_irq_types, &irq_idx); -- GitLab From ab229c2b72c35739e8ffb70af11190ff40f38701 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Fri, 11 Jul 2025 00:35:01 +0000 Subject: [PATCH 528/868] platform/chrome: cros_ec_typec: Add role swap ops Add the pr_set and dr_set typec_operations to registered typec ports. This enables sysfs to control power and data role when the port is capable of doing so. Signed-off-by: Abhishek Pandit-Subedi Co-developed-by: Radu Vele Signed-off-by: Radu Vele Link: https://lore.kernel.org/r/20250711003502.857536-1-raduvele@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_typec.c | 85 ++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index f437b594055cf..5a141401e4857 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -58,8 +58,91 @@ static int cros_typec_enter_usb_mode(struct typec_port *tc_port, enum usb_mode m &req, sizeof(req), NULL, 0); } +static int cros_typec_perform_role_swap(struct typec_port *tc_port, int target_role, u8 swap_type) +{ + struct cros_typec_port *port = typec_get_drvdata(tc_port); + struct cros_typec_data *data = port->typec_data; + struct ec_response_usb_pd_control_v2 resp; + struct ec_params_usb_pd_control req; + int role, ret; + + /* Must be at least v1 to support role swap. */ + if (!data->pd_ctrl_ver) + return -EOPNOTSUPP; + + /* First query the state */ + req.port = port->port_num; + req.role = USB_PD_CTRL_ROLE_NO_CHANGE; + req.mux = USB_PD_CTRL_MUX_NO_CHANGE; + req.swap = USB_PD_CTRL_SWAP_NONE; + + ret = cros_ec_cmd(data->ec, data->pd_ctrl_ver, EC_CMD_USB_PD_CONTROL, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + + switch (swap_type) { + case USB_PD_CTRL_SWAP_DATA: + role = (resp.role & PD_CTRL_RESP_ROLE_DATA) ? TYPEC_HOST : + TYPEC_DEVICE; + break; + case USB_PD_CTRL_SWAP_POWER: + role = (resp.role & PD_CTRL_RESP_ROLE_POWER) ? TYPEC_SOURCE : + TYPEC_SINK; + break; + default: + dev_warn(data->dev, "Unsupported role swap type %d\n", swap_type); + return -EOPNOTSUPP; + } + + if (role == target_role) + return 0; + + req.swap = swap_type; + ret = cros_ec_cmd(data->ec, data->pd_ctrl_ver, EC_CMD_USB_PD_CONTROL, + &req, sizeof(req), &resp, sizeof(resp)); + if (ret < 0) + return ret; + + switch (swap_type) { + case USB_PD_CTRL_SWAP_DATA: + role = resp.role & PD_CTRL_RESP_ROLE_DATA ? TYPEC_HOST : TYPEC_DEVICE; + if (role != target_role) { + dev_err(data->dev, "Data role swap failed despite EC returning success\n"); + return -EIO; + } + typec_set_data_role(tc_port, target_role); + break; + case USB_PD_CTRL_SWAP_POWER: + role = resp.role & PD_CTRL_RESP_ROLE_POWER ? TYPEC_SOURCE : TYPEC_SINK; + if (role != target_role) { + dev_err(data->dev, "Power role swap failed despite EC returning success\n"); + return -EIO; + } + typec_set_pwr_role(tc_port, target_role); + break; + default: + /* Should never execute */ + break; + } + + return 0; +} + +static int cros_typec_dr_swap(struct typec_port *port, enum typec_data_role role) +{ + return cros_typec_perform_role_swap(port, role, USB_PD_CTRL_SWAP_DATA); +} + +static int cros_typec_pr_swap(struct typec_port *port, enum typec_role role) +{ + return cros_typec_perform_role_swap(port, role, USB_PD_CTRL_SWAP_POWER); +} + static const struct typec_operations cros_typec_usb_mode_ops = { - .enter_usb_mode = cros_typec_enter_usb_mode + .enter_usb_mode = cros_typec_enter_usb_mode, + .dr_set = cros_typec_dr_swap, + .pr_set = cros_typec_pr_swap, }; static int cros_typec_parse_port_props(struct typec_capability *cap, -- GitLab From 8bb0a5fcde7b424172e48cf2b85664e7fe201417 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:43 +0200 Subject: [PATCH 529/868] ALSA: control: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-2-tiwai@suse.de --- sound/core/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/control.c b/sound/core/control.c index 11f660fc6f2b5..9c3fd5113a617 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1435,7 +1435,7 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, names = ue->priv_data; for (; item > 0; --item) names += strlen(names) + 1; - strcpy(uinfo->value.enumerated.name, names); + strscpy(uinfo->value.enumerated.name, names); return 0; } -- GitLab From d8cd23a0d2da68650a0710908aeefbe8b799fec3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:44 +0200 Subject: [PATCH 530/868] ALSA: rawmidi: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-3-tiwai@suse.de --- sound/core/rawmidi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 70a958ac1112c..20d36a346ccab 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -631,9 +631,9 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->flags = rmidi->info_flags; if (substream->inactive) info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE; - strcpy(info->id, rmidi->id); - strcpy(info->name, rmidi->name); - strcpy(info->subname, substream->name); + strscpy(info->id, rmidi->id); + strscpy(info->name, rmidi->name); + strscpy(info->subname, substream->name); info->subdevices_count = substream->pstr->substream_count; info->subdevices_avail = (substream->pstr->substream_count - substream->pstr->substream_opened); -- GitLab From 7df6224dec68eaa266663206d899a77d7a947541 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:45 +0200 Subject: [PATCH 531/868] ALSA: seq: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-4-tiwai@suse.de --- sound/core/seq/oss/seq_oss_init.c | 4 ++-- sound/core/seq/seq_clientmgr.c | 2 +- sound/core/seq/seq_midi.c | 2 +- sound/core/seq/seq_system.c | 4 ++-- sound/core/seq/seq_ump_client.c | 2 +- sound/core/seq/seq_virmidi.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index e6d7d83ed0e77..973f057eb731f 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -79,7 +79,7 @@ snd_seq_oss_create_client(void) system_client = rc; /* create announcement receiver port */ - strcpy(port->name, "Receiver"); + strscpy(port->name, "Receiver"); port->addr.client = system_client; port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */ port->type = 0; @@ -347,7 +347,7 @@ alloc_seq_queue(struct seq_oss_devinfo *dp) memset(&qinfo, 0, sizeof(qinfo)); qinfo.owner = system_client; qinfo.locked = 1; - strcpy(qinfo.name, "OSS Sequencer Emulation"); + strscpy(qinfo.name, "OSS Sequencer Emulation"); rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo); if (rc < 0) return rc; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 880240924bfd0..aa9c956d25819 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1256,7 +1256,7 @@ static void get_client_info(struct snd_seq_client *cptr, /* fill the info fields */ info->type = cptr->type; - strcpy(info->name, cptr->name); + strscpy(info->name, cptr->name); info->filter = cptr->filter; info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index ba52a77eda382..581e138a31159 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -344,7 +344,7 @@ snd_seq_midisynth_probe(struct device *_dev) info->stream = SNDRV_RAWMIDI_STREAM_INPUT; info->subdevice = p; if (snd_rawmidi_info_select(card, info) >= 0) - strcpy(port->name, info->subname); + strscpy(port->name, info->subname); if (! port->name[0]) { if (info->name[0]) { if (ports > 1) diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 853920f79016d..5b5603e5970b2 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -146,7 +146,7 @@ int __init snd_seq_system_client_init(void) } /* register timer */ - strcpy(port->name, "Timer"); + strscpy(port->name, "Timer"); port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */ port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */ port->kernel = &pcallbacks; @@ -160,7 +160,7 @@ int __init snd_seq_system_client_init(void) goto error_port; /* register announcement port */ - strcpy(port->name, "Announce"); + strscpy(port->name, "Announce"); port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ pcallbacks.event_input = NULL; pcallbacks.subscribe = sys_announce_subscribe; diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index 1255351b59ce1..27247babb16de 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -310,7 +310,7 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) SNDRV_SEQ_PORT_TYPE_HARDWARE | SNDRV_SEQ_PORT_TYPE_PORT; port->midi_channels = 16; - strcpy(port->name, "MIDI 2.0"); + strscpy(port->name, "MIDI 2.0"); memset(&pcallbacks, 0, sizeof(pcallbacks)); pcallbacks.owner = THIS_MODULE; pcallbacks.private_data = client; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index b4672613c2613..9e7fd4993a108 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -497,7 +497,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi &rmidi); if (err < 0) return err; - strcpy(rmidi->name, rmidi->id); + strscpy(rmidi->name, rmidi->id); rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (rdev == NULL) { snd_device_free(card, rmidi); -- GitLab From 59cea894bf3b80a4a3581ea614d465bf54474272 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:46 +0200 Subject: [PATCH 532/868] ALSA: mpu401: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-5-tiwai@suse.de --- sound/drivers/mpu401/mpu401.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index cd01af5fa4edf..d3f9424088d4f 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -63,8 +63,8 @@ static int snd_mpu401_create(struct device *devptr, int dev, 0, &card); if (err < 0) return err; - strcpy(card->driver, "MPU-401 UART"); - strcpy(card->shortname, card->driver); + strscpy(card->driver, "MPU-401 UART"); + strscpy(card->shortname, card->driver); sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]); if (irq[dev] >= 0) { sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]); -- GitLab From bb246ab0943d4a8253e001d03b607b046f7588b3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:47 +0200 Subject: [PATCH 533/868] ALSA: opl3: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-6-tiwai@suse.de --- sound/drivers/opl3/opl3_lib.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index 4e57e3b2f1189..cd9642a6689b0 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -245,7 +245,7 @@ static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no) tid.subdevice = 0; err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "AdLib timer #1"); + strscpy(timer->name, "AdLib timer #1"); timer->private_data = opl3; timer->hw = snd_opl3_timer1; } @@ -266,7 +266,7 @@ static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no) tid.subdevice = 0; err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "AdLib timer #2"); + strscpy(timer->name, "AdLib timer #2"); timer->private_data = opl3; timer->hw = snd_opl3_timer2; } @@ -497,18 +497,18 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, if (device == 0) hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; #endif - strcpy(hw->name, hw->id); + strscpy(hw->name, hw->id); switch (opl3->hardware & OPL3_HW_MASK) { case OPL3_HW_OPL2: - strcpy(hw->name, "OPL2 FM"); + strscpy(hw->name, "OPL2 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL2; break; case OPL3_HW_OPL3: - strcpy(hw->name, "OPL3 FM"); + strscpy(hw->name, "OPL3 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL3; break; case OPL3_HW_OPL4: - strcpy(hw->name, "OPL4 FM"); + strscpy(hw->name, "OPL4 FM"); hw->iface = SNDRV_HWDEP_IFACE_OPL4; break; } @@ -524,7 +524,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, #if IS_ENABLED(CONFIG_SND_SEQUENCER) if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) { - strcpy(opl3->seq_dev->name, hw->name); + strscpy(opl3->seq_dev->name, hw->name); *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3; } #endif -- GitLab From 0676ccf26f1cfb4a9aff96f933f52580357049db Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:48 +0200 Subject: [PATCH 534/868] ALSA: opl4: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-7-tiwai@suse.de --- sound/drivers/opl4/opl4_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 8fa61596875a5..ef6b2d533958a 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -152,7 +152,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device) opl4->seq_dev_num = seq_device; if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4, sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) { - strcpy(opl4->seq_dev->name, "OPL4 Wavetable"); + strscpy(opl4->seq_dev->name, "OPL4 Wavetable"); *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4; opl4->seq_dev->private_data = opl4; opl4->seq_dev->private_free = snd_opl4_seq_dev_free; -- GitLab From 6d352251e905c13f4363ca7780a94ee703077900 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:49 +0200 Subject: [PATCH 535/868] ALSA: pcsp: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-8-tiwai@suse.de --- sound/drivers/pcsp/pcsp.c | 4 ++-- sound/drivers/pcsp/pcsp_lib.c | 2 +- sound/drivers/pcsp/pcsp_mixer.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index e8482c2290c3d..ff6bb375c9001 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -122,8 +122,8 @@ static int snd_card_pcsp_probe(int devnum, struct device *dev) if (err < 0) return err; - strcpy(card->driver, "PC-Speaker"); - strcpy(card->shortname, "pcsp"); + strscpy(card->driver, "PC-Speaker"); + strscpy(card->shortname, "pcsp"); sprintf(card->longname, "Internal PC-Speaker at port 0x%x", pcsp_chip.port); diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index d9bc1ea1b53cc..80b313f4fcd36 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -340,7 +340,7 @@ int snd_pcsp_new_pcm(struct snd_pcsp *chip) chip->pcm->private_data = chip; chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - strcpy(chip->pcm->name, "pcsp"); + strscpy(chip->pcm->name, "pcsp"); snd_pcm_set_managed_buffer_all(chip->pcm, SNDRV_DMA_TYPE_CONTINUOUS, diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c index c0ae942358b9b..27d6150329a86 100644 --- a/sound/drivers/pcsp/pcsp_mixer.c +++ b/sound/drivers/pcsp/pcsp_mixer.c @@ -158,7 +158,7 @@ int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm) if (err < 0) return err; - strcpy(card->mixername, "PC-Speaker"); + strscpy(card->mixername, "PC-Speaker"); return 0; } -- GitLab From 7ba740ecf54cf1990e37607545d7e96673166f1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:50 +0200 Subject: [PATCH 536/868] ALSA: portman2x4: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-9-tiwai@suse.de --- sound/drivers/portman2x4.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 5e4ef25a83a49..b4fa6625a3d67 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -549,7 +549,7 @@ static int snd_portman_rawmidi_create(struct snd_card *card) return err; rmidi->private_data = pm; - strcpy(rmidi->name, CARD_NAME); + strscpy(rmidi->name, CARD_NAME); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -714,8 +714,8 @@ static int snd_portman_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Cannot create card\n"); return err; } - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, CARD_NAME); + strscpy(card->driver, DRIVER_NAME); + strscpy(card->shortname, CARD_NAME); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); -- GitLab From eb264bc529545756f314f14fa294681178f27fd9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:51 +0200 Subject: [PATCH 537/868] ALSA: serial-generic: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-10-tiwai@suse.de --- sound/drivers/serial-generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index 36409a56c675e..21ae053c05767 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -300,7 +300,7 @@ static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata, &snd_serial_generic_input); snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_serial_generic_output); - strcpy(rrawmidi->name, drvdata->card->shortname); + strscpy(rrawmidi->name, drvdata->card->shortname); snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], drvdata->serdev->ctrl->nr); @@ -329,7 +329,7 @@ static int snd_serial_generic_probe(struct serdev_device *serdev) if (err < 0) return err; - strcpy(card->driver, "SerialMIDI"); + strscpy(card->driver, "SerialMIDI"); sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr); sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr); -- GitLab From a725f6fa5835e518b1b52176894f62da7e587c45 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:52 +0200 Subject: [PATCH 538/868] ALSA: serial-u16550: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-11-tiwai@suse.de --- sound/drivers/serial-u16550.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 6d0656fcd574d..52772ccfc377b 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -845,7 +845,7 @@ static int snd_uart16550_rmidi(struct snd_uart16550 *uart, int device, &snd_uart16550_input); snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output); - strcpy(rrawmidi->name, "Serial MIDI"); + strscpy(rrawmidi->name, "Serial MIDI"); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | @@ -905,8 +905,8 @@ static int snd_serial_probe(struct platform_device *devptr) if (err < 0) return err; - strcpy(card->driver, "Serial"); - strcpy(card->shortname, "Serial MIDI (UART16550A)"); + strscpy(card->driver, "Serial"); + strscpy(card->shortname, "Serial MIDI (UART16550A)"); err = snd_uart16550_create(card, port[dev], irq[dev], speed[dev], base[dev], adaptor[dev], droponfull[dev], -- GitLab From 73e86d3666379b458e93e8a76fcd019f006b000d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:53 +0200 Subject: [PATCH 539/868] ALSA: virmidi: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-12-tiwai@suse.de --- sound/drivers/virmidi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 5f7b65ad63e3a..a204f42d10265 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -95,11 +95,11 @@ static int snd_virmidi_probe(struct platform_device *devptr) if (err < 0) return err; vmidi->midi[idx] = rmidi; - strcpy(rmidi->name, "Virtual Raw MIDI"); + strscpy(rmidi->name, "Virtual Raw MIDI"); } - strcpy(card->driver, "VirMIDI"); - strcpy(card->shortname, "VirMIDI"); + strscpy(card->driver, "VirMIDI"); + strscpy(card->shortname, "VirMIDI"); sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); err = snd_card_register(card); -- GitLab From c4a38b94bad184a140d357541e50576d4f288592 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:54 +0200 Subject: [PATCH 540/868] ALSA: vx: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-13-tiwai@suse.de --- sound/drivers/vx/vx_core.c | 2 +- sound/drivers/vx/vx_mixer.c | 2 +- sound/drivers/vx/vx_pcm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index e2def8719ed2c..7f25fa7c04043 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -806,7 +806,7 @@ struct vx_core *snd_vx_create(struct snd_card *card, chip->card = card; card->private_data = chip; - strcpy(card->driver, hw->name); + strscpy(card->driver, hw->name); sprintf(card->shortname, "Digigram %s", hw->name); vx_proc_init(chip); diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 53d78eb13c53b..0a51ecdc084a9 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -903,7 +903,7 @@ int snd_vx_mixer_new(struct vx_core *chip) struct snd_card *card = chip->card; char name[32]; - strcpy(card->mixername, card->driver); + strscpy(card->mixername, card->driver); /* output level controls */ for (i = 0; i < chip->hw->num_outs; i++) { diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index cbc77ca1ebdd5..defc489494af7 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1226,7 +1226,7 @@ int snd_vx_pcm_new(struct vx_core *chip) pcm->private_free = snd_vx_pcm_free; pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[i] = pcm; } -- GitLab From f9f63cb6eb91b67244c0fb5e91e98638178eadac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:55 +0200 Subject: [PATCH 541/868] ALSA: firewire: bebob: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-14-tiwai@suse.de --- sound/firewire/bebob/bebob.c | 6 +++--- sound/firewire/bebob/bebob_hwdep.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 2ba5962beb30f..4ebaeff164552 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -105,9 +105,9 @@ name_device(struct snd_bebob *bebob) if (err < 0) goto end; - strcpy(bebob->card->driver, "BeBoB"); - strcpy(bebob->card->shortname, model); - strcpy(bebob->card->mixername, model); + strscpy(bebob->card->driver, "BeBoB"); + strscpy(bebob->card->shortname, model); + strscpy(bebob->card->mixername, model); snprintf(bebob->card->longname, sizeof(bebob->card->longname), "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d", vendor, model, hw_id, revision, diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c index 6f9331655d431..5779e99a6bb23 100644 --- a/sound/firewire/bebob/bebob_hwdep.c +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -183,7 +183,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "BeBoB"); + strscpy(hwdep->name, "BeBoB"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; hwdep->ops = ops; hwdep->private_data = bebob; -- GitLab From ae2cdfc616722533332db129dbec385e32f467e0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:56 +0200 Subject: [PATCH 542/868] ALSA: firewire: dice: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-15-tiwai@suse.de --- sound/firewire/dice/dice-hwdep.c | 2 +- sound/firewire/dice/dice-pcm.c | 2 +- sound/firewire/dice/dice.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c index ffc0b97782d63..d165dd427bd3b 100644 --- a/sound/firewire/dice/dice-hwdep.c +++ b/sound/firewire/dice/dice-hwdep.c @@ -179,7 +179,7 @@ int snd_dice_create_hwdep(struct snd_dice *dice) err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); if (err < 0) return err; - strcpy(hwdep->name, "DICE"); + strscpy(hwdep->name, "DICE"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; hwdep->ops = ops; hwdep->private_data = dice; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 2cf2adb48f2a6..cfc19bd0d5dd4 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -442,7 +442,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) return err; pcm->private_data = dice; pcm->nonatomic = true; - strcpy(pcm->name, dice->card->shortname); + strscpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index d362e4251c688..9675ec14271d0 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -102,9 +102,9 @@ static void dice_card_strings(struct snd_dice *dice) unsigned int i; int err; - strcpy(card->driver, "DICE"); + strscpy(card->driver, "DICE"); - strcpy(card->shortname, "DICE"); + strscpy(card->shortname, "DICE"); BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME, card->shortname, @@ -117,16 +117,16 @@ static void dice_card_strings(struct snd_dice *dice) card->shortname[sizeof(card->shortname) - 1] = '\0'; } - strcpy(vendor, "?"); + strscpy(vendor, "?"); fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); - strcpy(model, "?"); + strscpy(model, "?"); fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); snprintf(card->longname, sizeof(card->longname), "%s %s (serial %u) at %s, S%d", vendor, model, dev->config_rom[4] & 0x3fffff, dev_name(&dice->unit->device), 100 << dev->max_speed); - strcpy(card->mixername, "DICE"); + strscpy(card->mixername, "DICE"); } static void dice_card_free(struct snd_card *card) -- GitLab From 662dacfc6466e9eb07ae6be18799b62dbdaa2a0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:57 +0200 Subject: [PATCH 543/868] ALSA: firewire: digi00x: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-16-tiwai@suse.de --- sound/firewire/digi00x/digi00x-hwdep.c | 2 +- sound/firewire/digi00x/digi00x.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c index aadf7d7248568..b150607c0a0d1 100644 --- a/sound/firewire/digi00x/digi00x-hwdep.c +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -188,7 +188,7 @@ int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) if (err < 0) return err; - strcpy(hwdep->name, "Digi00x"); + strscpy(hwdep->name, "Digi00x"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; hwdep->ops = ops; hwdep->private_data = dg00x; diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 704ae2a5316bd..cebc35dcf8cd1 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -30,9 +30,9 @@ static int name_card(struct snd_dg00x *dg00x) model = skip_spaces(name); - strcpy(dg00x->card->driver, "Digi00x"); - strcpy(dg00x->card->shortname, model); - strcpy(dg00x->card->mixername, model); + strscpy(dg00x->card->driver, "Digi00x"); + strscpy(dg00x->card->shortname, model); + strscpy(dg00x->card->mixername, model); snprintf(dg00x->card->longname, sizeof(dg00x->card->longname), "Digidesign %s, GUID %08x%08x at %s, S%d", model, fw_dev->config_rom[3], fw_dev->config_rom[4], -- GitLab From 4b366c9d78e70bbf2e52f0a2fa2aca3e505ce95e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:58 +0200 Subject: [PATCH 544/868] ALSA: firewire: fireface: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-17-tiwai@suse.de --- sound/firewire/fireface/ff-hwdep.c | 2 +- sound/firewire/fireface/ff.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index 8a741b3b0436f..ca5c5dee71f28 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -197,7 +197,7 @@ int snd_ff_create_hwdep_devices(struct snd_ff *ff) if (err < 0) return err; - strcpy(hwdep->name, ff->card->driver); + strscpy(hwdep->name, ff->card->driver); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE; hwdep->ops = hwdep_ops; hwdep->private_data = ff; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 6e84e4787259e..5d2c4fbf44341 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -27,9 +27,9 @@ static void name_card(struct snd_ff *ff) name = names[ff->unit_version]; - strcpy(ff->card->driver, "Fireface"); - strcpy(ff->card->shortname, name); - strcpy(ff->card->mixername, name); + strscpy(ff->card->driver, "Fireface"); + strscpy(ff->card->shortname, name); + strscpy(ff->card->mixername, name); snprintf(ff->card->longname, sizeof(ff->card->longname), "RME %s, GUID %08x%08x at %s, S%d", name, fw_dev->config_rom[3], fw_dev->config_rom[4], -- GitLab From fcd7979273af35d79999bfd0e897fa34d4b047d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:05:59 +0200 Subject: [PATCH 545/868] ALSA: firewire: fireworks: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-18-tiwai@suse.de --- sound/firewire/fireworks/fireworks.c | 6 +++--- sound/firewire/fireworks/fireworks_hwdep.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index e3ed4e094ccd0..69f722244362e 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -90,9 +90,9 @@ get_hardware_info(struct snd_efw *efw) (hwinfo->arm_version >> 16) & 0xff); efw->firmware_version = hwinfo->arm_version; - strcpy(efw->card->driver, "Fireworks"); - strcpy(efw->card->shortname, hwinfo->model_name); - strcpy(efw->card->mixername, hwinfo->model_name); + strscpy(efw->card->driver, "Fireworks"); + strscpy(efw->card->shortname, hwinfo->model_name); + strscpy(efw->card->mixername, hwinfo->model_name); scnprintf(efw->card->longname, sizeof(efw->card->longname), "%s %s v%s, GUID %08x%08x at %s, S%d", hwinfo->vendor_name, hwinfo->model_name, version, diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c index 3a53914277d35..037833cd066ef 100644 --- a/sound/firewire/fireworks/fireworks_hwdep.c +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -319,7 +319,7 @@ int snd_efw_create_hwdep_device(struct snd_efw *efw) err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "Fireworks"); + strscpy(hwdep->name, "Fireworks"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; hwdep->ops = ops; hwdep->private_data = efw; -- GitLab From 6e96433010646c71a9f14ea305bda46189360b3a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:00 +0200 Subject: [PATCH 546/868] ALSA: firewire: isight: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-19-tiwai@suse.de --- sound/firewire/isight.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index b1e059f0d4736..ee574b5d7406b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -455,7 +455,7 @@ static int isight_create_pcm(struct isight *isight) return err; pcm->private_data = isight; pcm->nonatomic = true; - strcpy(pcm->name, "iSight"); + strscpy(pcm->name, "iSight"); isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; isight->pcm->ops = &ops; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); @@ -638,13 +638,13 @@ static int isight_probe(struct fw_unit *unit, card->private_free = isight_card_free; - strcpy(card->driver, "iSight"); - strcpy(card->shortname, "Apple iSight"); + strscpy(card->driver, "iSight"); + strscpy(card->shortname, "Apple iSight"); snprintf(card->longname, sizeof(card->longname), "Apple iSight (GUID %08x%08x) at %s, S%d", fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&unit->device), 100 << fw_dev->max_speed); - strcpy(card->mixername, "iSight"); + strscpy(card->mixername, "iSight"); err = isight_create_pcm(isight); if (err < 0) -- GitLab From 97f54683816dad23896c3a93a83253044369f3f0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:01 +0200 Subject: [PATCH 547/868] ALSA: firewire: motu: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-20-tiwai@suse.de --- sound/firewire/motu/motu-hwdep.c | 2 +- sound/firewire/motu/motu-pcm.c | 2 +- sound/firewire/motu/motu.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index 88d1f4b56e4be..1ed60618220d2 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -290,7 +290,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu) if (err < 0) return err; - strcpy(hwdep->name, "MOTU"); + strscpy(hwdep->name, "MOTU"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU; hwdep->ops = ops; hwdep->private_data = motu; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index f3b48495acae2..7b4d476af348f 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -361,7 +361,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) return err; pcm->private_data = motu; pcm->nonatomic = true; - strcpy(pcm->name, motu->card->shortname); + strscpy(pcm->name, motu->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index d14ab5dd5bea4..fd2a9dddbfa6f 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -41,9 +41,9 @@ static void name_card(struct snd_motu *motu) } } - strcpy(motu->card->driver, "FW-MOTU"); - strcpy(motu->card->shortname, motu->spec->name); - strcpy(motu->card->mixername, motu->spec->name); + strscpy(motu->card->driver, "FW-MOTU"); + strscpy(motu->card->shortname, motu->spec->name); + strscpy(motu->card->mixername, motu->spec->name); snprintf(motu->card->longname, sizeof(motu->card->longname), "MOTU %s (version:%06x), GUID %08x%08x at %s, S%d", motu->spec->name, version, -- GitLab From 74e4255b2058642eb7913d9da442cee6b740cae9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:02 +0200 Subject: [PATCH 548/868] ALSA: firewire: oxfw: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-21-tiwai@suse.de --- sound/firewire/oxfw/oxfw-hwdep.c | 2 +- sound/firewire/oxfw/oxfw-pcm.c | 2 +- sound/firewire/oxfw/oxfw.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c index a0fe996185549..3be214d8a9228 100644 --- a/sound/firewire/oxfw/oxfw-hwdep.c +++ b/sound/firewire/oxfw/oxfw-hwdep.c @@ -177,7 +177,7 @@ int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw) err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, oxfw->card->driver); + strscpy(hwdep->name, oxfw->card->driver); hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW; hwdep->ops = hwdep_ops; hwdep->private_data = oxfw; diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 8ca9dde54ec6f..e13dc817fc280 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -441,7 +441,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) pcm->private_data = oxfw; pcm->nonatomic = true; - strcpy(pcm->name, oxfw->card->shortname); + strscpy(pcm->name, oxfw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); if (cap > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 98ae0e8cba879..7a985f3cb8f61 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -105,9 +105,9 @@ static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *ent m = model; } - strcpy(oxfw->card->driver, d); - strcpy(oxfw->card->mixername, m); - strcpy(oxfw->card->shortname, m); + strscpy(oxfw->card->driver, d); + strscpy(oxfw->card->mixername, m); + strscpy(oxfw->card->shortname, m); scnprintf(oxfw->card->longname, sizeof(oxfw->card->longname), "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", -- GitLab From c03ebbdfddc1a8ae5a50a77819069688f0cffbe3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:03 +0200 Subject: [PATCH 549/868] ALSA: firewire: tascam: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Reviewed-by: Takashi Sakamoto Link: https://patch.msgid.link/20250710100727.22653-22-tiwai@suse.de --- sound/firewire/tascam/tascam-hwdep.c | 2 +- sound/firewire/tascam/tascam.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 74eed9505665c..8fc30cba29d53 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -265,7 +265,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) if (err < 0) return err; - strcpy(hwdep->name, "Tascam"); + strscpy(hwdep->name, "Tascam"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM; hwdep->ops = ops; hwdep->private_data = tscm; diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index 86880089de286..4f68bb4c58bcd 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -73,9 +73,9 @@ static int identify_model(struct snd_tscm *tscm) if (tscm->spec == NULL) return -ENODEV; - strcpy(tscm->card->driver, "FW-TASCAM"); - strcpy(tscm->card->shortname, model); - strcpy(tscm->card->mixername, model); + strscpy(tscm->card->driver, "FW-TASCAM"); + strscpy(tscm->card->shortname, model); + strscpy(tscm->card->mixername, model); snprintf(tscm->card->longname, sizeof(tscm->card->longname), "TASCAM %s, GUID %08x%08x at %s, S%d", model, fw_dev->config_rom[3], fw_dev->config_rom[4], -- GitLab From 94d7b8beae3574fe7167b8e0f31d1d480442ccdf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:04 +0200 Subject: [PATCH 550/868] ALSA: ad1816a: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-23-tiwai@suse.de --- sound/isa/ad1816a/ad1816a.c | 4 ++-- sound/isa/ad1816a/ad1816a_lib.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 5c9e2d41d9005..8e84d4091f1ee 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -143,8 +143,8 @@ static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard, if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000) chip->clock_freq = clockfreq[dev]; - strcpy(card->driver, "AD1816A"); - strcpy(card->shortname, "ADI SoundPort AD1816A"); + strscpy(card->driver, "AD1816A"); + strscpy(card->shortname, "ADI SoundPort AD1816A"); sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d", card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 2b87036cb94fb..400ae547bcbab 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -631,7 +631,7 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device) pcm->private_data = chip; pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; - strcpy(pcm->name, snd_ad1816a_chip_id(chip)); + strscpy(pcm->name, snd_ad1816a_chip_id(chip)); snd_ad1816a_init(chip); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev, @@ -655,7 +655,7 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device) error = snd_timer_new(chip->card, "AD1816A", &tid, &timer); if (error < 0) return error; - strcpy(timer->name, snd_ad1816a_chip_id(chip)); + strscpy(timer->name, snd_ad1816a_chip_id(chip)); timer->private_data = chip; chip->timer = timer; timer->hw = snd_ad1816a_timer_table; @@ -912,7 +912,7 @@ int snd_ad1816a_mixer(struct snd_ad1816a *chip) card = chip->card; - strcpy(card->mixername, snd_ad1816a_chip_id(chip)); + strscpy(card->mixername, snd_ad1816a_chip_id(chip)); for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip)); -- GitLab From 461cef4d7d0270b5d11c2738c3a0a4e099351718 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:05 +0200 Subject: [PATCH 551/868] ALSA: adlib: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-24-tiwai@suse.de --- sound/isa/adlib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c index f079ba4ef1a0d..03fb2bce92556 100644 --- a/sound/isa/adlib.c +++ b/sound/isa/adlib.c @@ -61,8 +61,8 @@ static int snd_adlib_probe(struct device *dev, unsigned int n) return -EBUSY; } - strcpy(card->driver, DEV_NAME); - strcpy(card->shortname, CRD_NAME); + strscpy(card->driver, DEV_NAME); + strscpy(card->shortname, CRD_NAME); sprintf(card->longname, CRD_NAME " at %#lx", port[n]); error = snd_opl3_create(card, port[n], port[n] + 2, OPL3_HW_AUTO, 1, &opl3); -- GitLab From dd9de1a144e10d5498ea1f0fb4ab70fa6d953ff0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:06 +0200 Subject: [PATCH 552/868] ALSA: als100: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-25-tiwai@suse.de --- sound/isa/als100.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/isa/als100.c b/sound/isa/als100.c index e70dbf0b099ab..cfc241bd252e7 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c @@ -192,14 +192,14 @@ static int snd_card_als100_probe(int dev, acard->chip = chip; if (pid->driver_data == SB_HW_DT019X) { - strcpy(card->driver, "DT-019X"); - strcpy(card->shortname, "Diamond Tech. DT-019X"); + strscpy(card->driver, "DT-019X"); + strscpy(card->shortname, "Diamond Tech. DT-019X"); snprintf(card->longname, sizeof(card->longname), "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d", chip->name, chip->port, irq[dev], dma8[dev]); } else { - strcpy(card->driver, "ALS100"); - strcpy(card->shortname, "Avance Logic ALS100"); + strscpy(card->driver, "ALS100"); + strscpy(card->shortname, "Avance Logic ALS100"); snprintf(card->longname, sizeof(card->longname), "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d", chip->name, chip->port, irq[dev], dma8[dev], -- GitLab From 231d135055a685e75b7d815e2aacdf85cb5bcc3e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:07 +0200 Subject: [PATCH 553/868] ALSA: cmi8328: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-26-tiwai@suse.de --- sound/isa/cmi8328.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index d30cce4cc7e39..4e6d823af103a 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -155,34 +155,34 @@ static int snd_cmi8328_mixer(struct snd_wss *chip) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* rename AUX0 switch to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX0 volume to CD */ - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX1 switch to Synth */ - strcpy(id1.name, "Aux Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); return err; } /* rename AUX1 volume to Synth */ - strcpy(id1.name, "Aux Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); id1.index = 1; - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "error renaming control\n"); @@ -362,8 +362,8 @@ static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev) if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0) dev_warn(pdev, "error initializing OPL3 hwdep\n"); - strcpy(card->driver, "CMI8328"); - strcpy(card->shortname, "C-Media CMI8328"); + strscpy(card->driver, "CMI8328"); + strscpy(card->shortname, "C-Media CMI8328"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d", card->shortname, cmi->wss->port, irq[ndev], dma1[ndev], (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]); -- GitLab From 4e55e03e4c17150218c6869cee7cb754bd291522 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:08 +0200 Subject: [PATCH 554/868] ALSA: cmi8330: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-27-tiwai@suse.de --- sound/isa/cmi8330.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 25b4dc181089c..e681c2c824744 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -297,7 +297,7 @@ static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard) unsigned int idx; int err; - strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); + strscpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { err = snd_ctl_add(card, @@ -437,7 +437,7 @@ static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip) err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm); if (err < 0) return err; - strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); + strscpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330"); pcm->private_data = chip; /* SB16 */ @@ -590,8 +590,8 @@ static int snd_cmi8330_probe(struct snd_card *card, int dev) mpuport[dev]); } - strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); - strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); + strscpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D"); + strscpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D"); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", card->shortname, acard->wss->port, -- GitLab From 9a5cca8d838bc67da5c742ab1776d952364eccb5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:09 +0200 Subject: [PATCH 555/868] ALSA: cs423x: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-28-tiwai@suse.de --- sound/isa/cs423x/cs4236.c | 2 +- sound/isa/cs423x/cs4236_lib.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index ad20bb2649bd5..e36cc147651a4 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -510,7 +510,7 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev, return -ENODEV; /* prepare second id */ - strcpy(cid, pdev->id[0].id); + strscpy(cid, pdev->id[0].id); cid[5] = '1'; cdev = NULL; list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) { diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index 1a03cff6915b8..eaaf39aae5263 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -1023,7 +1023,7 @@ int snd_cs4236_mixer(struct snd_wss *chip) if (snd_BUG_ON(!chip || !chip->card)) return -EINVAL; card = chip->card; - strcpy(card->mixername, snd_wss_chip_id(chip)); + strscpy(card->mixername, snd_wss_chip_id(chip)); if (chip->hardware == WSS_HW_CS4235 || chip->hardware == WSS_HW_CS4239) { -- GitLab From 22095c0515e0cb34738e1600b54f8bceb7cf7b42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:10 +0200 Subject: [PATCH 556/868] ALSA: es1688: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-29-tiwai@suse.de --- sound/isa/es1688/es1688_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index c0c230149a750..2ef183f197adc 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -706,7 +706,7 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - strcpy(pcm->name, snd_es1688_chip_id(chip)); + strscpy(pcm->name, snd_es1688_chip_id(chip)); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, @@ -971,7 +971,7 @@ int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip) if (snd_BUG_ON(!chip || !card)) return -EINVAL; - strcpy(card->mixername, snd_es1688_chip_id(chip)); + strscpy(card->mixername, snd_es1688_chip_id(chip)); for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip)); -- GitLab From 962dba349a76dd6872371c793f074704e57d2dc9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:11 +0200 Subject: [PATCH 557/868] ALSA: es18xx: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-30-tiwai@suse.de --- sound/isa/es18xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index e35c727a52fa2..3e89a84c62447 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1771,7 +1771,7 @@ static int snd_es18xx_mixer(struct snd_card *card) int err; unsigned int idx; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) { struct snd_kcontrol *kctl; -- GitLab From 74987a0cc4614477a9a5279f645a6d4b0dd66d74 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:12 +0200 Subject: [PATCH 558/868] ALSA: galaxy: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-31-tiwai@suse.de --- sound/isa/galaxy/galaxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c index 3164eb8510fa4..b0f1562d0fc0d 100644 --- a/sound/isa/galaxy/galaxy.c +++ b/sound/isa/galaxy/galaxy.c @@ -542,8 +542,8 @@ static int __snd_galaxy_probe(struct device *dev, unsigned int n) return err; } - strcpy(card->driver, DRV_NAME); - strcpy(card->shortname, DRV_NAME); + strscpy(card->driver, DRV_NAME); + strscpy(card->shortname, DRV_NAME); sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", card->shortname, port[n], wss_port[n], irq[n], dma1[n], dma2[n]); -- GitLab From 72b1baa1d650dd8b15c8a60daff2e37889d0e627 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:13 +0200 Subject: [PATCH 559/868] ALSA: gus: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-32-tiwai@suse.de --- sound/isa/gus/gus_main.c | 16 ++++++++-------- sound/isa/gus/gus_mixer.c | 2 +- sound/isa/gus/gus_pcm.c | 2 +- sound/isa/gus/gus_timer.c | 4 ++-- sound/isa/gus/gus_uart.c | 2 +- sound/isa/gus/gusextreme.c | 8 ++++---- sound/isa/gus/gusmax.c | 16 ++++++++-------- sound/isa/gus/interwave.c | 34 +++++++++++++++++----------------- 8 files changed, 42 insertions(+), 42 deletions(-) diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 7166869e423d9..873ef4046cd69 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -348,8 +348,8 @@ static int snd_gus_check_version(struct snd_gus_card * gus) rev = inb(GUSP(gus, BOARDVERSION)); spin_unlock_irqrestore(&gus->reg_lock, flags); dev_dbg(card->dev, "GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev); - strcpy(card->driver, "GUS"); - strcpy(card->longname, "Gravis UltraSound Classic (2.4)"); + strscpy(card->driver, "GUS"); + strscpy(card->longname, "Gravis UltraSound Classic (2.4)"); if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { if (rev >= 5 && rev <= 9) { gus->ics_flag = 1; @@ -360,16 +360,16 @@ static int snd_gus_check_version(struct snd_gus_card * gus) } if (rev >= 10 && rev != 255) { if (rev >= 10 && rev <= 11) { - strcpy(card->driver, "GUS MAX"); - strcpy(card->longname, "Gravis UltraSound MAX"); + strscpy(card->driver, "GUS MAX"); + strscpy(card->longname, "Gravis UltraSound MAX"); gus->max_flag = 1; } else if (rev == 0x30) { - strcpy(card->driver, "GUS ACE"); - strcpy(card->longname, "Gravis UltraSound Ace"); + strscpy(card->driver, "GUS ACE"); + strscpy(card->longname, "Gravis UltraSound Ace"); gus->ace_flag = 1; } else if (rev == 0x50) { - strcpy(card->driver, "GUS Extreme"); - strcpy(card->longname, "Gravis UltraSound Extreme"); + strscpy(card->driver, "GUS Extreme"); + strscpy(card->longname, "Gravis UltraSound Extreme"); gus->ess_flag = 1; } else { dev_err(card->dev, diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c index 03f9cfcbf6010..60c3a8219770d 100644 --- a/sound/isa/gus/gus_mixer.c +++ b/sound/isa/gus/gus_mixer.c @@ -152,7 +152,7 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus) if (gus->ics_flag) snd_component_add(card, "ICS2101"); if (card->mixername[0] == '\0') { - strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); + strscpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); } else { if (gus->ics_flag) strcat(card->mixername, ",ICS2101"); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 16f9bbb43a544..8b9b7b8d92b22 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -851,7 +851,7 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) SNDRV_DMA_TYPE_DEV, card->dev, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } - strcpy(pcm->name, pcm->id); + strscpy(pcm->name, pcm->id); if (gus->interwave) { sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A'); } diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index 047ddbc6192fc..7267fb5bf8e5e 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -156,7 +156,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus) tid.subdevice = 0; if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { - strcpy(timer->name, "GF1 timer #1"); + strscpy(timer->name, "GF1 timer #1"); timer->private_data = gus; timer->private_free = snd_gf1_timer1_free; timer->hw = snd_gf1_timer1; @@ -166,7 +166,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus) tid.device++; if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) { - strcpy(timer->name, "GF1 timer #2"); + strscpy(timer->name, "GF1 timer #2"); timer->private_data = gus; timer->private_free = snd_gf1_timer2_free; timer->hw = snd_gf1_timer2; diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 08276509447fe..e207f274f240f 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -236,7 +236,7 @@ int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device) err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); + strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index 6eab95bd49c1d..28827a2e6cbdb 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -204,15 +204,15 @@ static int snd_gusextreme_mixer(struct snd_card *card) id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); error = snd_ctl_rename_id(card, &id1, &id2); if (error < 0) return error; /* reassign Master Playback Switch to Synth Playback Switch */ - strcpy(id1.name, "Master Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Master Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); error = snd_ctl_rename_id(card, &id1, &id2); if (error < 0) return error; diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index 445fd2fb50f10..b572411c44228 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -134,24 +134,24 @@ static int snd_gusmax_mixer(struct snd_wss *chip) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUXA to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUXB to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 18a98123e2867..0e0bcd85a648d 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -271,7 +271,7 @@ static int snd_interwave_detect(struct snd_interwave *iwcard, dev_dbg(gus->card->dev, "[0x%lx] InterWave check - passed\n", gus->gf1.port); gus->interwave = 1; - strcpy(gus->card->shortname, "AMD InterWave"); + strscpy(gus->card->shortname, "AMD InterWave"); gus->revision = rev1 >> 4; #ifndef SNDRV_STB return 0; /* ok.. We have an InterWave board */ @@ -500,11 +500,11 @@ static int snd_interwave_mixer(struct snd_wss *chip) id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; #if 0 /* remove mono microphone controls */ - strcpy(id1.name, "Mic Playback Switch"); + strscpy(id1.name, "Mic Playback Switch"); err = snd_ctl_remove_id(card, &id1); if (err < 0) return err; - strcpy(id1.name, "Mic Playback Volume"); + strscpy(id1.name, "Mic Playback Volume"); err = snd_ctl_remove_id(card, &id1); if (err < 0) return err; @@ -520,24 +520,24 @@ static int snd_interwave_mixer(struct snd_wss *chip) snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); /* reassign AUXA to SYNTHESIZER */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "Synth Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "Synth Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "Synth Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "Synth Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUXB to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -713,14 +713,14 @@ static int snd_interwave_probe(struct snd_card *card, int dev, memset(&id1, 0, sizeof(id1)); memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id1.name, "Master Playback Switch"); - strcpy(id2.name, id1.name); + strscpy(id1.name, "Master Playback Switch"); + strscpy(id2.name, id1.name); id2.index = 1; err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Master Playback Volume"); - strcpy(id2.name, id1.name); + strscpy(id1.name, "Master Playback Volume"); + strscpy(id2.name, id1.name); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -742,8 +742,8 @@ static int snd_interwave_probe(struct snd_card *card, int dev, #else str = "InterWave STB"; #endif - strcpy(card->driver, str); - strcpy(card->shortname, str); + strscpy(card->driver, str); + strscpy(card->shortname, str); sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d", str, gus->gf1.port, -- GitLab From 9c4e42967dc5c358d4bec463b71a9ac7c7b9e855 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:14 +0200 Subject: [PATCH 560/868] ALSA: msnd: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-33-tiwai@suse.de --- sound/isa/msnd/msnd.c | 2 +- sound/isa/msnd/msnd_pinnacle.c | 22 +++++++++++----------- sound/isa/msnd/msnd_pinnacle_mixer.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c index 69c515421dd81..8c1d2e27854bc 100644 --- a/sound/isa/msnd/msnd.c +++ b/sound/isa/msnd/msnd.c @@ -673,7 +673,7 @@ int snd_msnd_pcm(struct snd_card *card, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, "Hurricane"); + strscpy(pcm->name, "Hurricane"); return 0; } diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 8caf431677e58..969bbb18657b9 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -221,8 +221,8 @@ static int snd_msnd_probe(struct snd_card *card) } #ifdef MSND_CLASSIC - strcpy(card->shortname, "Classic/Tahiti/Monterey"); - strcpy(card->longname, "Turtle Beach Multisound"); + strscpy(card->shortname, "Classic/Tahiti/Monterey"); + strscpy(card->longname, "Turtle Beach Multisound"); dev_info(card->dev, LOGNAME ": %s, " "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n", card->shortname, @@ -251,38 +251,38 @@ static int snd_msnd_probe(struct snd_card *card) switch (info & 0x7) { case 0x0: rev = "I"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x1: rev = "F"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x2: rev = "G"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x3: rev = "H"; - strcpy(card->shortname, pin); + strscpy(card->shortname, pin); break; case 0x4: rev = "E"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x5: rev = "C"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x6: rev = "D"; - strcpy(card->shortname, fiji); + strscpy(card->shortname, fiji); break; case 0x7: rev = "A-B (Fiji) or A-E (Pinnacle)"; - strcpy(card->shortname, pinfiji); + strscpy(card->shortname, pinfiji); break; } - strcpy(card->longname, "Turtle Beach Multisound Pinnacle"); + strscpy(card->longname, "Turtle Beach Multisound Pinnacle"); dev_info(card->dev, LOGNAME ": %s revision %s, Xilinx version %s, " "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n", card->shortname, diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index 63633bd41e5b6..2f1bb5a2b376d 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -299,7 +299,7 @@ int snd_msndmix_new(struct snd_card *card) if (snd_BUG_ON(!chip)) return -EINVAL; spin_lock_init(&chip->mixer_lock); - strcpy(card->mixername, "MSND Pinnacle Mixer"); + strscpy(card->mixername, "MSND Pinnacle Mixer"); for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { err = snd_ctl_add(card, -- GitLab From c7eaa0ebed3589fe416aac22b2deb23c7ee4ff4f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:15 +0200 Subject: [PATCH 561/868] ALSA: opl3sa2: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-34-tiwai@suse.de --- sound/isa/opl3sa2.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index a5ed5aa0606f1..5e8e1326d5c01 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -488,30 +488,30 @@ static int snd_opl3sa2_mixer(struct snd_card *card) memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } /* reassign AUX1 to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); return err; } - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "FM Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opl3sa2 control\n"); @@ -618,8 +618,8 @@ static int snd_opl3sa2_card_new(struct device *pdev, int dev, sizeof(struct snd_opl3sa2), &card); if (err < 0) return err; - strcpy(card->driver, "OPL3SA2"); - strcpy(card->shortname, "Yamaha OPL3-SA"); + strscpy(card->driver, "OPL3SA2"); + strscpy(card->shortname, "Yamaha OPL3-SA"); chip = card->private_data; spin_lock_init(&chip->reg_lock); chip->irq = -1; -- GitLab From b4a82a97c4d4126143e08966405c6b3b653d3b0d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:16 +0200 Subject: [PATCH 562/868] ALSA: opti9xx: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-35-tiwai@suse.de --- sound/isa/opti9xx/miro.c | 8 ++++---- sound/isa/opti9xx/opti92x-ad1848.c | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index 31d736d1dd101..ad7180d7c0c2b 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -718,10 +718,10 @@ static int snd_miro_mixer(struct snd_card *card, switch (miro->hardware) { case OPTi9XX_HW_82C924: - strcpy(card->mixername, "ACI & OPTi924"); + strscpy(card->mixername, "ACI & OPTi924"); break; case OPTi9XX_HW_82C929: - strcpy(card->mixername, "ACI & OPTi929"); + strscpy(card->mixername, "ACI & OPTi929"); break; default: snd_BUG(); @@ -779,7 +779,7 @@ static int snd_miro_init(struct snd_miro *chip, static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; chip->hardware = hardware; - strcpy(chip->name, snd_opti9xx_names[hardware]); + strscpy(chip->name, snd_opti9xx_names[hardware]); chip->mc_base_size = opti9xx_mc_size[hardware]; @@ -1351,7 +1351,7 @@ static int snd_miro_probe(struct snd_card *card) sprintf(card->shortname, "unknown Cardinal Technologies"); } - strcpy(card->driver, "miro"); + strscpy(card->driver, "miro"); scnprintf(card->longname, sizeof(card->longname), "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", card->shortname, miro->name, codec->pcm->name, diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index a07a665d93dca..328d043a16193 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -171,7 +171,7 @@ static int snd_opti9xx_init(struct snd_opti9xx *chip, static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; chip->hardware = hardware; - strcpy(chip->name, snd_opti9xx_names[hardware]); + strscpy(chip->name, snd_opti9xx_names[hardware]); spin_lock_init(&chip->lock); @@ -594,35 +594,35 @@ static int snd_opti93x_mixer(struct snd_wss *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); memset(&id1, 0, sizeof(id1)); memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 switch to CD */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opti93x control\n"); return err; } /* reassign AUX1 switch to FM */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) { dev_err(card->dev, "Cannot rename opti93x control\n"); return err; } /* remove AUX1 volume */ - strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; + strscpy(id1.name, "Aux Playback Volume"); id1.index = 1; snd_ctl_remove_id(card, &id1); /* Replace WSS volume controls with OPTi93x volume controls */ id1.index = 0; for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { - strcpy(id1.name, snd_opti93x_controls[idx].name); + strscpy(id1.name, snd_opti93x_controls[idx].name); snd_ctl_remove_id(card, &id1); err = snd_ctl_add(card, @@ -857,7 +857,7 @@ static int snd_opti9xx_probe(struct snd_card *card) #endif chip->irq = irq; card->sync_irq = chip->irq; - strcpy(card->driver, chip->name); + strscpy(card->driver, chip->name); sprintf(card->shortname, "OPTi %s", card->driver); #if defined(CS4231) || defined(OPTi93X) scnprintf(card->longname, sizeof(card->longname), -- GitLab From c8eef317e66bb958eaa3b156386fdd3962706bf0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:17 +0200 Subject: [PATCH 563/868] ALSA: sc6000: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-36-tiwai@suse.de --- sound/isa/sc6000.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 3115c32b4061b..6d618cc2ba457 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -469,24 +469,24 @@ static int snd_sc6000_mixer(struct snd_wss *chip) id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER; id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 to FM */ - strcpy(id1.name, "Aux Playback Switch"); - strcpy(id2.name, "FM Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); + strscpy(id2.name, "FM Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "FM Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "FM Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; /* reassign AUX1 to CD */ - strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; - strcpy(id2.name, "CD Playback Switch"); + strscpy(id1.name, "Aux Playback Switch"); id1.index = 1; + strscpy(id2.name, "CD Playback Switch"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; - strcpy(id1.name, "Aux Playback Volume"); - strcpy(id2.name, "CD Playback Volume"); + strscpy(id1.name, "Aux Playback Volume"); + strscpy(id2.name, "CD Playback Volume"); err = snd_ctl_rename_id(card, &id1, &id2); if (err < 0) return err; @@ -652,8 +652,8 @@ static int __snd_sc6000_probe(struct device *devptr, unsigned int dev) mpu_port[dev]); } - strcpy(card->driver, DRV_NAME); - strcpy(card->shortname, "SC-6000"); + strscpy(card->driver, DRV_NAME); + strscpy(card->shortname, "SC-6000"); sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d", mss_port[dev], xirq, xdma); -- GitLab From 96b1776d019a7f1865fc3078c68650654319b509 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:18 +0200 Subject: [PATCH 564/868] ALSA: sscape: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-37-tiwai@suse.de --- sound/isa/sscape.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 09120e38f4c27..709a1659d66f5 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -1047,8 +1047,8 @@ static int create_sscape(int dev, struct snd_card *card) wss_port[dev], irq[dev]); return err; } - strcpy(card->driver, "SoundScape"); - strcpy(card->shortname, name); + strscpy(card->driver, "SoundScape"); + strscpy(card->shortname, name); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n", name, sscape->chip->port, sscape->chip->irq, -- GitLab From 2e2a2feb915c90d2a2be96373339157e11a614fb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:19 +0200 Subject: [PATCH 565/868] ALSA: wavefront: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-38-tiwai@suse.de --- sound/isa/wavefront/wavefront.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 621ab420a60f9..07c68568091d1 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -234,7 +234,7 @@ static struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card, if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0) return NULL; - strcpy (wavefront_synth->name, + strscpy (wavefront_synth->name, "WaveFront (ICS2115) wavetable synthesizer"); wavefront_synth->ops.open = snd_wavefront_synth_open; wavefront_synth->ops.release = snd_wavefront_synth_release; @@ -292,10 +292,10 @@ static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card, return NULL; if (mpu == internal_mpu) { - strcpy(rmidi->name, "WaveFront MIDI (Internal)"); + strscpy(rmidi->name, "WaveFront MIDI (Internal)"); rmidi->private_data = &internal_id; } else { - strcpy(rmidi->name, "WaveFront MIDI (External)"); + strscpy(rmidi->name, "WaveFront MIDI (External)"); rmidi->private_data = &external_id; } @@ -407,7 +407,7 @@ snd_wavefront_probe (struct snd_card *card, int dev) return -ENOMEM; } - strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); + strscpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer"); wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115; hw_dev++; @@ -478,12 +478,12 @@ snd_wavefront_probe (struct snd_card *card, int dev) hw_dev++; - strcpy(card->driver, "Tropez+"); - strcpy(card->shortname, "Turtle Beach Tropez+"); + strscpy(card->driver, "Tropez+"); + strscpy(card->shortname, "Turtle Beach Tropez+"); } else { /* Need a way to distinguish between Maui and Tropez */ - strcpy(card->driver, "WaveFront"); - strcpy(card->shortname, "Turtle Beach WaveFront"); + strscpy(card->driver, "WaveFront"); + strscpy(card->shortname, "Turtle Beach WaveFront"); } /* ----- Register the card --------- */ -- GitLab From fc52ff69c5f1b144bfc9edaa7160fde993c874f5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:20 +0200 Subject: [PATCH 566/868] ALSA: wss: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-39-tiwai@suse.de --- sound/isa/wss/wss_lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 9c655789574d0..1b6a80021d182 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -1865,7 +1865,7 @@ int snd_wss_pcm(struct snd_wss *chip, int device) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; if (chip->hardware != WSS_HW_INTERWAVE) pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, snd_wss_chip_id(chip)); + strscpy(pcm->name, snd_wss_chip_id(chip)); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); @@ -1896,7 +1896,7 @@ int snd_wss_timer(struct snd_wss *chip, int device) err = snd_timer_new(chip->card, "CS4231", &tid, &timer); if (err < 0) return err; - strcpy(timer->name, snd_wss_chip_id(chip)); + strscpy(timer->name, snd_wss_chip_id(chip)); timer->private_data = chip; timer->private_free = snd_wss_timer_free; timer->hw = snd_wss_timer_table; @@ -2176,7 +2176,7 @@ int snd_wss_mixer(struct snd_wss *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); /* Use only the first 11 entries on AD1848 */ if (chip->hardware & WSS_HW_AD1848_MASK) -- GitLab From 7169b6a6763d5a879f42ce1a1aad72b6d611e375 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:21 +0200 Subject: [PATCH 567/868] ALSA: mips: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-40-tiwai@suse.de --- sound/mips/hal2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index 991793e6bda9b..f88e6a6733a56 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -706,7 +706,7 @@ static int hal2_pcm_create(struct snd_hal2 *hal2) return err; pcm->private_data = hal2; - strcpy(pcm->name, "SGI HAL2"); + strscpy(pcm->name, "SGI HAL2"); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -862,8 +862,8 @@ static int hal2_probe(struct platform_device *pdev) return err; } - strcpy(card->driver, "SGI HAL2 Audio"); - strcpy(card->shortname, "SGI HAL2 Audio"); + strscpy(card->driver, "SGI HAL2 Audio"); + strscpy(card->shortname, "SGI HAL2 Audio"); sprintf(card->longname, "%s irq %i", card->shortname, SGI_HPCDMA_IRQ); -- GitLab From c9b41682945f3c389bd230aa74bd1409df31b521 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:22 +0200 Subject: [PATCH 568/868] ALSA: parisc: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-41-tiwai@suse.de --- sound/parisc/harmony.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index db9c296dd688e..76dd2210f9eac 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -601,7 +601,7 @@ snd_harmony_pcm_init(struct snd_harmony *h) pcm->private_data = h; pcm->info_flags = 0; - strcpy(pcm->name, "harmony"); + strscpy(pcm->name, "harmony"); h->pcm = pcm; h->psubs = NULL; @@ -823,7 +823,7 @@ snd_harmony_mixer_init(struct snd_harmony *h) if (snd_BUG_ON(!h)) return -EINVAL; card = h->card; - strcpy(card->mixername, "Harmony Gain control interface"); + strscpy(card->mixername, "Harmony Gain control interface"); for (idx = 0; idx < HARMONY_CONTROLS; idx++) { err = snd_ctl_add(card, @@ -937,8 +937,8 @@ snd_harmony_probe(struct parisc_device *padev) if (err < 0) goto free_and_ret; - strcpy(card->driver, "harmony"); - strcpy(card->shortname, "Harmony"); + strscpy(card->driver, "harmony"); + strscpy(card->shortname, "Harmony"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, h->hpa, h->irq); -- GitLab From c9b7c4b628817f8aae757924210f55374362e91d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:23 +0200 Subject: [PATCH 569/868] ALSA: ac97: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-42-tiwai@suse.de --- sound/pci/ac97/ac97_codec.c | 4 ++-- sound/pci/ac97/ac97_patch.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 88ac37739b765..588c094080bea 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -2303,7 +2303,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, if (ac97_is_audio(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); @@ -2324,7 +2324,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, if (ac97_is_modem(ac97)) { char comp[16]; if (card->mixername[0] == '\0') { - strcpy(card->mixername, name); + strscpy(card->mixername, name); } else { if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) { strcat(card->mixername, ","); diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index cd83aa864ea3e..3002be9d88f30 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -298,7 +298,7 @@ static int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control - Wide"); + strscpy(kctl->id.name, "3D Control - Wide"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); err = snd_ctl_add(ac97->bus->card, @@ -891,7 +891,7 @@ static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97)); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; @@ -906,13 +906,13 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97) err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0); kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97); err = snd_ctl_add(ac97->bus->card, kctl); if (err < 0) return err; - strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); + strscpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; -- GitLab From 48102e32203178012567e6b78192e707d5ca7668 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:24 +0200 Subject: [PATCH 570/868] ALSA: ad1889: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-43-tiwai@suse.de --- sound/pci/ad1889.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index ac27a93ce4ff1..020cbb467e7e6 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -605,7 +605,7 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->psubs = NULL; @@ -866,8 +866,8 @@ __snd_ad1889_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "AD1889"); - strcpy(card->shortname, "Analog Devices AD1889"); + strscpy(card->driver, "AD1889"); + strscpy(card->shortname, "Analog Devices AD1889"); /* (3) */ err = snd_ad1889_create(card, pci); -- GitLab From 1d68ba24a79c2dce7cdb4bfbc6fdd8e0b2e69b85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:25 +0200 Subject: [PATCH 571/868] ALSA: ak4531: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-44-tiwai@suse.de --- sound/pci/ak4531_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c index 6af88e7b86f81..e54812bfb2c6e 100644 --- a/sound/pci/ak4531_codec.c +++ b/sound/pci/ak4531_codec.c @@ -389,7 +389,7 @@ int snd_ak4531_mixer(struct snd_card *card, snd_ak4531_free(ak4531); return err; } - strcpy(card->mixername, "Asahi Kasei AK4531"); + strscpy(card->mixername, "Asahi Kasei AK4531"); ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */ udelay(100); ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */ -- GitLab From 3edc76a671f761f6797e55cab88157f43256dedd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:26 +0200 Subject: [PATCH 572/868] ALSA: ali5451: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-45-tiwai@suse.de --- sound/pci/ali5451/ali5451.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 69c02bdd38ce1..a6e499e0ceda9 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1645,7 +1645,7 @@ static int snd_ali_pcm(struct snd_ali *codec, int device, pcm->info_flags = 0; pcm->dev_class = desc->class; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, desc->name); + strscpy(pcm->name, desc->name); codec->pcm[0] = pcm; return 0; } @@ -2133,8 +2133,8 @@ static int __snd_ali_probe(struct pci_dev *pci, snd_ali_proc_init(codec); - strcpy(card->driver, "ALI5451"); - strcpy(card->shortname, "ALI 5451"); + strscpy(card->driver, "ALI5451"); + strscpy(card->shortname, "ALI 5451"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, codec->port, codec->irq); -- GitLab From 03b0a614e2292a4afcaf5d196752d02803e2e6ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:27 +0200 Subject: [PATCH 573/868] ALSA: als300: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-46-tiwai@suse.de --- sound/pci/als300.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 43f98719e61b7..f9e8424dc77f8 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -546,7 +546,7 @@ static int snd_als300_new_pcm(struct snd_als300 *chip) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, "ALS300"); + strscpy(pcm->name, "ALS300"); chip->pcm = pcm; /* set operators */ @@ -705,7 +705,7 @@ static int snd_als300_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "ALS300"); + strscpy(card->driver, "ALS300"); if (chip->chip_type == DEVICE_ALS300_PLUS) /* don't know much about ALS300+ yet * print revision number for now */ -- GitLab From 1813fa7c587a35aa1d73e1d6d4f1599ecac8bd33 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:28 +0200 Subject: [PATCH 574/868] ALSA: als4000: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-47-tiwai@suse.de --- sound/pci/als4000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 3f4f3037f71f4..eb159497c905a 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -877,8 +877,8 @@ static int __snd_card_als4000_probe(struct pci_dev *pci, snd_als4000_configure(chip); - strcpy(card->driver, "ALS4000"); - strcpy(card->shortname, "Avance Logic ALS4000"); + strscpy(card->driver, "ALS4000"); + strscpy(card->shortname, "Avance Logic ALS4000"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->alt_port, chip->irq); -- GitLab From 1882c12ae2ab01d52b33d72b7b3203c44819a8f9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:29 +0200 Subject: [PATCH 575/868] ALSA: asihpi: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-48-tiwai@suse.de --- sound/pci/asihpi/asihpi.c | 10 +++++----- sound/pci/asihpi/hpi6000.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index cbd964f87349e..8419f2b6e5891 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1257,7 +1257,7 @@ static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device) pcm->private_data = asihpi; pcm->info_flags = 0; - strcpy(pcm->name, "Asihpi PCM"); + strscpy(pcm->name, "Asihpi PCM"); /*? do we want to emulate MMAP for non-BBM cards? Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ @@ -2310,7 +2310,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, clkcache->s[uinfo->value.enumerated.item].name); return 0; } @@ -2530,7 +2530,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (snd_BUG_ON(!asihpi)) return -EINVAL; card = asihpi->card; - strcpy(card->mixername, "Asihpi Mixer"); + strscpy(card->mixername, "Asihpi Mixer"); err = hpi_mixer_open(asihpi->hpi->adapter->index, @@ -2741,7 +2741,7 @@ static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); if (err < 0) return err; - strcpy(hw->name, "asihpi (HPI)"); + strscpy(hw->name, "asihpi (HPI)"); hw->iface = SNDRV_HWDEP_IFACE_LAST; hw->ops.open = snd_asihpi_hpi_open; hw->ops.ioctl = snd_asihpi_hpi_ioctl; @@ -2889,7 +2889,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, by enable_hwdep module param*/ snd_asihpi_hpi_new(asihpi, 0); - strcpy(card->driver, "ASIHPI"); + strscpy(card->driver, "ASIHPI"); sprintf(card->shortname, "AudioScience ASI%4X", asihpi->hpi->adapter->type); diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 72aa135d69f85..b08578c93c6a2 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -608,7 +608,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao, phr->u.ax.assert.p2 = 0; phr->u.ax.assert.count = 1; /* assert count */ phr->u.ax.assert.dsp_index = -1; /* "dsp index" */ - strcpy(phr->u.ax.assert.sz_message, "PCI2040 error"); + strscpy(phr->u.ax.assert.sz_message, "PCI2040 error"); phr->u.ax.assert.dsp_msg_addr = 0; gw_pci_read_asserts = 0; gw_pci_write_asserts = 0; -- GitLab From 2dc364f96536b7b5e6c32f8dc5fa4917d92acf4c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:30 +0200 Subject: [PATCH 576/868] ALSA: atiixp: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-49-tiwai@suse.de --- sound/pci/atiixp.c | 10 +++++----- sound/pci/atiixp_modem.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 427006be240bd..4f544950ee7bf 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1271,7 +1271,7 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP AC97"); + strscpy(pcm->name, "ATI IXP AC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1301,9 +1301,9 @@ static int snd_atiixp_pcm_new(struct atiixp *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops); pcm->private_data = chip; if (chip->spdif_over_aclink) - strcpy(pcm->name, "ATI IXP IEC958 (AC97)"); + strscpy(pcm->name, "ATI IXP IEC958 (AC97)"); else - strcpy(pcm->name, "ATI IXP IEC958 (Direct)"); + strscpy(pcm->name, "ATI IXP IEC958 (Direct)"); chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1576,8 +1576,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); - strcpy(card->shortname, "ATI IXP"); + strscpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA"); + strscpy(card->shortname, "ATI IXP"); err = snd_atiixp_init(card, pci); if (err < 0) return err; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 8d3083b9b024d..f7417c2bb4776 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -982,7 +982,7 @@ static int snd_atiixp_pcm_new(struct atiixp_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, "ATI IXP MC97"); + strscpy(pcm->name, "ATI IXP MC97"); chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1206,8 +1206,8 @@ static int __snd_atiixp_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ATIIXP-MODEM"); - strcpy(card->shortname, "ATI IXP Modem"); + strscpy(card->driver, "ATIIXP-MODEM"); + strscpy(card->shortname, "ATI IXP Modem"); err = snd_atiixp_init(card, pci); if (err < 0) return err; -- GitLab From 5cd156964fe731f0c852ed5c51b9b64aaf046a53 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:31 +0200 Subject: [PATCH 577/868] ALSA: au88x0: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-50-tiwai@suse.de --- sound/pci/au88x0/au88x0.c | 4 ++-- sound/pci/au88x0/au88x0_mixer.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index fd986247331a4..de56e83d8e10c 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -220,7 +220,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_workaround(pci, pcifix[dev]); // Card details needed in snd_vortex_midi - strcpy(card->driver, CARD_NAME_SHORT); + strscpy(card->driver, CARD_NAME_SHORT); sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->io, chip->irq); @@ -270,7 +270,7 @@ __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) snd_vortex_synth_arg_t *arg; arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); - strcpy(wave->name, "Aureal Synth"); + strscpy(wave->name, "Aureal Synth"); arg->hwptr = vortex; arg->index = 1; arg->seq_ports = seq_ports[dev]; diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index aeba684b8d182..00781a7fd28c7 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -15,7 +15,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } -- GitLab From f6e41e48d994f4e569df2f4be69f12e9b5641b87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:32 +0200 Subject: [PATCH 578/868] ALSA: aw2: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-51-tiwai@suse.de --- sound/pci/aw2/aw2-alsa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 7b4b8f785517c..1d7aab14579e1 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -281,8 +281,8 @@ static int snd_aw2_probe(struct pci_dev *pci, /* init spinlock */ spin_lock_init(&chip->reg_lock); /* (4) Define driver ID and name string */ - strcpy(card->driver, "aw2"); - strcpy(card->shortname, "Audiowerk2"); + strscpy(card->driver, "aw2"); + strscpy(card->shortname, "Audiowerk2"); sprintf(card->longname, "%s with SAA7146 irq %i", card->shortname, chip->irq); @@ -509,7 +509,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA]; /* Set PCM device name */ - strcpy(pcm_playback_ana->name, "Analog playback"); + strscpy(pcm_playback_ana->name, "Analog playback"); /* Associate private data to PCM device */ pcm_playback_ana->private_data = pcm_device; /* set operators of PCM device */ @@ -541,7 +541,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG]; /* Set PCM device name */ - strcpy(pcm_playback_num->name, "Digital playback"); + strscpy(pcm_playback_num->name, "Digital playback"); /* Associate private data to PCM device */ pcm_playback_num->private_data = pcm_device; /* set operators of PCM device */ @@ -574,7 +574,7 @@ static int snd_aw2_new_pcm(struct aw2 *chip) pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA]; /* Set PCM device name */ - strcpy(pcm_capture->name, "Capture"); + strscpy(pcm_capture->name, "Capture"); /* Associate private data to PCM device */ pcm_capture->private_data = pcm_device; /* set operators of PCM device */ -- GitLab From 8b1208d70ae49c56e7742f172ae6f39fcebca2ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:33 +0200 Subject: [PATCH 579/868] ALSA: azt3328: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-52-tiwai@suse.de --- sound/pci/azt3328.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 053a18f434bfb..4418b9ae33e60 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1188,7 +1188,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) return err; } snd_component_add(card, "AZF3328 mixer"); - strcpy(card->mixername, "AZF3328 mixer"); + strscpy(card->mixername, "AZF3328 mixer"); return 0; } @@ -2095,7 +2095,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); /* same pcm object for playback/capture (see snd_pcm_new() above) */ chip->pcm[AZF_CODEC_PLAYBACK] = pcm; chip->pcm[AZF_CODEC_CAPTURE] = pcm; @@ -2112,7 +2112,7 @@ snd_azf3328_pcm(struct snd_azf3328 *chip) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[AZF_CODEC_I2S_OUT] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -2217,7 +2217,7 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device) if (err < 0) goto out; - strcpy(timer->name, "AZF3328 timer"); + strscpy(timer->name, "AZF3328 timer"); timer->private_data = chip; timer->hw = snd_azf3328_timer_hw; @@ -2437,8 +2437,8 @@ __snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) return err; chip = card->private_data; - strcpy(card->driver, "AZF3328"); - strcpy(card->shortname, "Aztech AZF3328 (PCI168)"); + strscpy(card->driver, "AZF3328"); + strscpy(card->shortname, "Aztech AZF3328 (PCI168)"); err = snd_azf3328_create(card, pci, pci_id->driver_data); if (err < 0) -- GitLab From dd4fcc8f04929a53a8001915266a7beccc5e0652 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:34 +0200 Subject: [PATCH 580/868] ALSA: bt87x: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-53-tiwai@suse.de --- sound/pci/bt87x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 91492dd2b38a1..b70f6f4ffe676 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -672,7 +672,7 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name) if (err < 0) return err; pcm->private_data = chip; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, @@ -872,12 +872,12 @@ static int __snd_bt87x_probe(struct pci_dev *pci, chip->board.no_analog ? "no " : "", chip->board.no_digital ? "no " : "", chip->board.dig_rate); - strcpy(card->driver, "Bt87x"); + strscpy(card->driver, "Bt87x"); sprintf(card->shortname, "Brooktree Bt%x", pci->device); sprintf(card->longname, "%s at %#llx, irq %i", card->shortname, (unsigned long long)pci_resource_start(pci, 0), chip->irq); - strcpy(card->mixername, "Bt87x"); + strscpy(card->mixername, "Bt87x"); err = snd_card_register(card); if (err < 0) -- GitLab From 32aeb8606936d4f928aa28fb85c2785b5fa5870e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:35 +0200 Subject: [PATCH 581/868] ALSA: ca0106: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-54-tiwai@suse.de --- sound/pci/ca0106/ca0106_main.c | 6 +++--- sound/pci/ca0106/ca0106_mixer.c | 4 ++-- sound/pci/ca0106/ca_midi.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 7c7119cad63cd..2426187931818 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1315,7 +1315,7 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device) } pcm->info_flags = 0; - strcpy(pcm->name, "CA0106"); + strscpy(pcm->name, "CA0106"); for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; @@ -1617,8 +1617,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card, pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model, pci->revision, chip->serial); - strcpy(card->driver, "CA0106"); - strcpy(card->shortname, "CA0106"); + strscpy(card->driver, "CA0106"); + strscpy(card->shortname, "CA0106"); for (c = ca0106_chip_details; c->serial; c++) { if (subsystem[dev]) { diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 1d5a899b2c243..f7b6b2db889b1 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -701,7 +701,7 @@ static int remove_ctl(struct snd_card *card, const char *name) { struct snd_ctl_elem_id id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + strscpy(id.name, name); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(card, &id); } @@ -849,7 +849,7 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu) return err; } - strcpy(card->mixername, "CA0106"); + strscpy(card->mixername, "CA0106"); return 0; } diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index 957e60f648211..f9cec67f31ac6 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -287,7 +287,7 @@ int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name) spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | -- GitLab From e43c8878e9dd7f9dc0b75c8f57bdeaa351efe3ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:36 +0200 Subject: [PATCH 582/868] ALSA: cmipci: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-55-tiwai@suse.de --- sound/pci/cmipci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index b00df0a60d3f4..9056214e9cdae 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -1868,7 +1868,7 @@ static int snd_cmipci_pcm_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI DAC/ADC"); + strscpy(pcm->name, "C-Media PCI DAC/ADC"); cm->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1890,7 +1890,7 @@ static int snd_cmipci_pcm2_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI 2nd DAC"); + strscpy(pcm->name, "C-Media PCI 2nd DAC"); cm->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1913,7 +1913,7 @@ static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device) pcm->private_data = cm; pcm->info_flags = 0; - strcpy(pcm->name, "C-Media PCI IEC958"); + strscpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2633,7 +2633,7 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) card = cm->card; - strcpy(card->mixername, "CMedia PCI"); + strscpy(card->mixername, "CMedia PCI"); spin_lock_irq(&cm->reg_lock); snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ @@ -3216,14 +3216,14 @@ static int snd_cmipci_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_CMEDIA_CM8738: case PCI_DEVICE_ID_CMEDIA_CM8738B: - strcpy(card->driver, "CMI8738"); + strscpy(card->driver, "CMI8738"); break; case PCI_DEVICE_ID_CMEDIA_CM8338A: case PCI_DEVICE_ID_CMEDIA_CM8338B: - strcpy(card->driver, "CMI8338"); + strscpy(card->driver, "CMI8338"); break; default: - strcpy(card->driver, "CMIPCI"); + strscpy(card->driver, "CMIPCI"); break; } -- GitLab From e3502b8672cafc679b57717e4f9c1abca8106061 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:37 +0200 Subject: [PATCH 583/868] ALSA: cs4281: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-56-tiwai@suse.de --- sound/pci/cs4281.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 90958a422b75e..31cb9cbe2f031 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -950,7 +950,7 @@ static int snd_cs4281_pcm(struct cs4281 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "CS4281"); + strscpy(pcm->name, "CS4281"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, @@ -1715,7 +1715,7 @@ static int snd_cs4281_midi(struct cs4281 *chip, int device) err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS4281"); + strscpy(rmidi->name, "CS4281"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -1870,8 +1870,8 @@ static int __snd_cs4281_probe(struct pci_dev *pci, if (err < 0) return err; snd_cs4281_create_gameport(chip); - strcpy(card->driver, "CS4281"); - strcpy(card->shortname, "Cirrus Logic CS4281"); + strscpy(card->driver, "CS4281"); + strscpy(card->shortname, "Cirrus Logic CS4281"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->ba0_addr, -- GitLab From 0eb71ea6d7f3e64137c19e8d4a3bdcffc57c5b05 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:38 +0200 Subject: [PATCH 584/868] ALSA: cs46xx: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-57-tiwai@suse.de --- sound/pci/cs46xx/cs46xx.c | 4 ++-- sound/pci/cs46xx/cs46xx_lib.c | 20 ++++++++++---------- sound/pci/cs46xx/dsp_spos.c | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 8634004a606b6..9c1995737eb7f 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -107,8 +107,8 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, snd_cs46xx_gameport(chip); - strcpy(card->driver, "CS46xx"); - strcpy(card->shortname, "Sound Fusion CS46xx"); + strscpy(card->driver, "CS46xx"); + strscpy(card->shortname, "Sound Fusion CS46xx"); sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", card->shortname, chip->ba0_addr, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fb733633740b4..85a7988cf8221 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1760,7 +1760,7 @@ int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx"); + strscpy(pcm->name, "CS46xx"); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1787,7 +1787,7 @@ int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Rear"); + strscpy(pcm->name, "CS46xx - Rear"); chip->pcm_rear = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1812,7 +1812,7 @@ int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - Center LFE"); + strscpy(pcm->name, "CS46xx - Center LFE"); chip->pcm_center_lfe = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1837,7 +1837,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "CS46xx - IEC958"); + strscpy(pcm->name, "CS46xx - IEC958"); chip->pcm_iec958 = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2672,7 +2672,7 @@ int snd_cs46xx_midi(struct snd_cs46xx *chip, int device) err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, "CS46XX"); + strscpy(rmidi->name, "CS46XX"); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -3853,27 +3853,27 @@ int snd_cs46xx_create(struct snd_card *card, } region = &chip->region.name.ba0; - strcpy(region->name, "CS46xx_BA0"); + strscpy(region->name, "CS46xx_BA0"); region->base = chip->ba0_addr; region->size = CS46XX_BA0_SIZE; region = &chip->region.name.data0; - strcpy(region->name, "CS46xx_BA1_data0"); + strscpy(region->name, "CS46xx_BA1_data0"); region->base = chip->ba1_addr + BA1_SP_DMEM0; region->size = CS46XX_BA1_DATA0_SIZE; region = &chip->region.name.data1; - strcpy(region->name, "CS46xx_BA1_data1"); + strscpy(region->name, "CS46xx_BA1_data1"); region->base = chip->ba1_addr + BA1_SP_DMEM1; region->size = CS46XX_BA1_DATA1_SIZE; region = &chip->region.name.pmem; - strcpy(region->name, "CS46xx_BA1_pmem"); + strscpy(region->name, "CS46xx_BA1_pmem"); region->base = chip->ba1_addr + BA1_SP_PMEM; region->size = CS46XX_BA1_PRG_SIZE; region = &chip->region.name.reg; - strcpy(region->name, "CS46xx_BA1_reg"); + strscpy(region->name, "CS46xx_BA1_reg"); region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 1db6bc58d6a61..e07f85322f1ce 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -203,7 +203,7 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type) index = find_free_symbol_index (ins); - strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); + strscpy (ins->symbol_table.symbols[index].symbol_name, symbol_name); ins->symbol_table.symbols[index].address = address; ins->symbol_table.symbols[index].symbol_type = type; ins->symbol_table.symbols[index].module = NULL; @@ -923,7 +923,7 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam index = find_free_scb_index (ins); memset(&ins->scbs[index], 0, sizeof(ins->scbs[index])); - strcpy(ins->scbs[index].scb_name, name); + strscpy(ins->scbs[index].scb_name, name); ins->scbs[index].address = dest; ins->scbs[index].index = index; ins->scbs[index].ref_count = 1; @@ -953,9 +953,9 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) } if (name) - strcpy(ins->tasks[ins->ntask].task_name, name); + strscpy(ins->tasks[ins->ntask].task_name, name); else - strcpy(ins->tasks[ins->ntask].task_name, "(NULL)"); + strscpy(ins->tasks[ins->ntask].task_name, "(NULL)"); ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].size = size; -- GitLab From 2d5239eab8ee2d474b5c1a70475f6fe87c74ba57 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:39 +0200 Subject: [PATCH 585/868] ALSA: cs5530: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-58-tiwai@suse.de --- sound/pci/cs5530.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 532891e67c347..292b65aa758a9 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -207,8 +207,8 @@ static int snd_cs5530_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "CS5530"); - strcpy(card->shortname, "CS5530 Audio"); + strscpy(card->driver, "CS5530"); + strscpy(card->shortname, "CS5530 Audio"); sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); err = snd_card_register(card); -- GitLab From fe9502be46f7b76b9d95fc5df7b018f48b8a22ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:40 +0200 Subject: [PATCH 586/868] ALSA: cs5535audio: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-59-tiwai@suse.de --- sound/pci/cs5535audio/cs5535audio.c | 4 ++-- sound/pci/cs5535audio/cs5535audio_pcm.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 0f319013a2a2f..76566e7baea0a 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -315,9 +315,9 @@ static int __snd_cs5535audio_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "CS5535 Audio"); + strscpy(card->shortname, "CS5535 Audio"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, cs5535au->port, cs5535au->irq); diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 9c88e99e3750d..f296b2c630265 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -423,7 +423,7 @@ int snd_cs5535audio_pcm(struct cs5535audio *cs5535au) pcm->private_data = cs5535au; pcm->info_flags = 0; - strcpy(pcm->name, "CS5535 Audio"); + strscpy(pcm->name, "CS5535 Audio"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &cs5535au->pci->dev, -- GitLab From ea9deed52d7f196454456f775fddf53328e90f2a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:41 +0200 Subject: [PATCH 587/868] ALSA: ctxfi: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-60-tiwai@suse.de --- sound/pci/ctxfi/ctmixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 6797fde3d7889..496682613db57 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -1219,7 +1219,7 @@ int ct_alsa_mix_create(struct ct_atc *atc, if (err) return err; - strcpy(atc->card->mixername, device_name); + strscpy(atc->card->mixername, device_name); return 0; } -- GitLab From 7bab02a32c6ae08aeb60aa6f85363cd5847d0911 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:42 +0200 Subject: [PATCH 588/868] ALSA: echoaudio: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-61-tiwai@suse.de --- sound/pci/echoaudio/midi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index c3f3c91295619..4ee230794b4ee 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -311,7 +311,7 @@ static int snd_echo_midi_create(struct snd_card *card, if (err < 0) return err; - strcpy(chip->rmidi->name, card->shortname); + strscpy(chip->rmidi->name, card->shortname); chip->rmidi->private_data = chip; snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, -- GitLab From 3ddbb87d99946b6d21ded58f983fb4642ad57845 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:43 +0200 Subject: [PATCH 589/868] ALSA: emu10k1: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-62-tiwai@suse.de --- sound/pci/emu10k1/emumpu401.c | 2 +- sound/pci/emu10k1/emupcm.c | 10 +++++----- sound/pci/emu10k1/p16v.c | 2 +- sound/pci/emu10k1/timer.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 747c34b3d566a..efff19bbc0e9c 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -320,7 +320,7 @@ static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *m spin_lock_init(&midi->open_lock); spin_lock_init(&midi->input_lock); spin_lock_init(&midi->output_lock); - strcpy(rmidi->name, name); + strscpy(rmidi->name, name); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 1bf6e3d652f81..5414148057ea7 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1425,7 +1425,7 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "ADC Capture/Standard PCM Playback"); + strscpy(pcm->name, "ADC Capture/Standard PCM Playback"); emu->pcm = pcm; /* playback substream can't use managed buffers due to alignment */ @@ -1457,7 +1457,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Multichannel Playback"); + strscpy(pcm->name, "Multichannel Playback"); emu->pcm_multi = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) @@ -1491,7 +1491,7 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); pcm->info_flags = 0; - strcpy(pcm->name, "Mic Capture"); + strscpy(pcm->name, "Mic Capture"); emu->pcm_mic = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev, @@ -1818,9 +1818,9 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; if (emu->audigy) - strcpy(pcm->name, "Multichannel Capture"); + strscpy(pcm->name, "Multichannel Capture"); else - strcpy(pcm->name, "Multichannel Capture/PT Playback"); + strscpy(pcm->name, "Multichannel Capture/PT Playback"); emu->pcm_efx = pcm; if (!emu->card_capabilities->emu_model) { diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index a9a75891f1da4..e774174d10de7 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -573,7 +573,7 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "p16v"); + strscpy(pcm->name, "p16v"); emu->pcm_p16v = pcm; emu->p16v_interrupt = snd_p16v_interrupt; diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index bb2478319361a..1231ae2bf9313 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -80,7 +80,7 @@ int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device) tid.subdevice = 0; err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "EMU10K1 timer"); + strscpy(timer->name, "EMU10K1 timer"); timer->private_data = emu; timer->hw = snd_emu10k1_timer_hw; } -- GitLab From d24457cae235e97d00fd6305d76b3858d2390a8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:44 +0200 Subject: [PATCH 590/868] ALSA: ens1370: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-63-tiwai@suse.de --- sound/pci/ens1370.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 1e6adf1ae3043..82e10ecb91966 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1244,7 +1244,7 @@ static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device) pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC2/ADC"); + strscpy(pcm->name, CHIP_NAME " DAC2/ADC"); ensoniq->pcm1 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1276,7 +1276,7 @@ static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device) #endif pcm->private_data = ensoniq; pcm->info_flags = 0; - strcpy(pcm->name, CHIP_NAME " DAC1"); + strscpy(pcm->name, CHIP_NAME " DAC1"); ensoniq->pcm2 = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2250,7 +2250,7 @@ static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device) err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, CHIP_NAME); + strscpy(rmidi->name, CHIP_NAME); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | @@ -2346,9 +2346,9 @@ static int __snd_audiopci_probe(struct pci_dev *pci, snd_ensoniq_create_gameport(ensoniq, dev); - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, "Ensoniq AudioPCI"); + strscpy(card->shortname, "Ensoniq AudioPCI"); sprintf(card->longname, "%s %s at 0x%lx, irq %i", card->shortname, card->driver, -- GitLab From 6df1d279dbfb0498ec984e55d11d91c56728ad77 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:45 +0200 Subject: [PATCH 591/868] ALSA: es1938: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-64-tiwai@suse.de --- sound/pci/es1938.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 27728bdfac577..0ce7076206f92 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -982,7 +982,7 @@ static int snd_es1938_new_pcm(struct es1938 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "ESS Solo-1"); + strscpy(pcm->name, "ESS Solo-1"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 64*1024, 64*1024); @@ -1658,7 +1658,7 @@ static int snd_es1938_mixer(struct es1938 *chip) card = chip->card; - strcpy(card->mixername, "ESS Solo-1"); + strscpy(card->mixername, "ESS Solo-1"); for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { struct snd_kcontrol *kctl; @@ -1720,8 +1720,8 @@ static int __snd_es1938_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "ES1938"); - strcpy(card->shortname, "ESS ES1938 (Solo-1)"); + strscpy(card->driver, "ES1938"); + strscpy(card->shortname, "ESS ES1938 (Solo-1)"); sprintf(card->longname, "%s rev %i, irq %i", card->shortname, chip->revision, -- GitLab From 42b68e73568d3ddb4b571d9277fceeff91aded0b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:46 +0200 Subject: [PATCH 592/868] ALSA: es1968: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-65-tiwai@suse.de --- sound/pci/es1968.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 37bac890613ee..624ba7d475662 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1812,7 +1812,7 @@ snd_es1968_pcm(struct es1968 *chip, int device) pcm->info_flags = 0; - strcpy(pcm->name, "ESS Maestro"); + strscpy(pcm->name, "ESS Maestro"); chip->pcm = pcm; @@ -2761,16 +2761,16 @@ static int __snd_es1968_probe(struct pci_dev *pci, switch (chip->type) { case TYPE_MAESTRO2E: - strcpy(card->driver, "ES1978"); - strcpy(card->shortname, "ESS ES1978 (Maestro 2E)"); + strscpy(card->driver, "ES1978"); + strscpy(card->shortname, "ESS ES1978 (Maestro 2E)"); break; case TYPE_MAESTRO2: - strcpy(card->driver, "ES1968"); - strcpy(card->shortname, "ESS ES1968 (Maestro 2)"); + strscpy(card->driver, "ES1968"); + strscpy(card->shortname, "ESS ES1968 (Maestro 2)"); break; case TYPE_MAESTRO: - strcpy(card->driver, "ESM1"); - strcpy(card->shortname, "ESS Maestro 1"); + strscpy(card->driver, "ESM1"); + strscpy(card->shortname, "ESS Maestro 1"); break; } -- GitLab From 7deb4eac7d65a37c138076565f2717c2a6c63342 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:47 +0200 Subject: [PATCH 593/868] ALSA: fm801: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-66-tiwai@suse.de --- sound/pci/fm801.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index f283256eda0db..cf40bd06b7341 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -726,7 +726,7 @@ static int snd_fm801_pcm(struct fm801 *chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "FM801"); + strscpy(pcm->name, "FM801"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev, @@ -1291,8 +1291,8 @@ static int __snd_card_fm801_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "FM801"); - strcpy(card->shortname, "ForteMedia FM801-"); + strscpy(card->driver, "FM801"); + strscpy(card->shortname, "ForteMedia FM801-"); strcat(card->shortname, chip->multichannel ? "AU" : "AS"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); -- GitLab From b51681287f9c9fb14436d3705e268b1d65c19b2d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:48 +0200 Subject: [PATCH 594/868] ALSA: ice1712: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-67-tiwai@suse.de --- sound/pci/ice1712/ice1712.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index a8ac148876763..1aefd46ebf6b5 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -850,7 +850,7 @@ static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer"); + strscpy(pcm->name, "ICE1712 consumer"); ice->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -875,7 +875,7 @@ static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 consumer (DS)"); + strscpy(pcm->name, "ICE1712 consumer (DS)"); ice->pcm_ds = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1216,7 +1216,7 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1712 multi"); + strscpy(pcm->name, "ICE1712 multi"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -2559,8 +2559,8 @@ static int snd_ice1712_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1712"); - strcpy(card->shortname, "ICEnsemble ICE1712"); + strscpy(card->driver, "ICE1712"); + strscpy(card->shortname, "ICEnsemble ICE1712"); err = snd_ice1712_create(card, pci, model[dev], omni[dev], cs8427_timeout[dev], dxr_enable[dev]); @@ -2570,9 +2570,9 @@ static int snd_ice1712_probe(struct pci_dev *pci, for (tbl = card_tables; *tbl; tbl++) { for (c = *tbl; c->subvendor; c++) { if (c->subvendor == ice->eeprom.subvendor) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) -- GitLab From f79d7aef3e4e260bef96be3638f9b37f9c70fdf1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:49 +0200 Subject: [PATCH 595/868] ALSA: ice1724: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-68-tiwai@suse.de --- sound/pci/ice1712/ice1724.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index be22b159e65a1..0445d2e8e5485 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1118,7 +1118,7 @@ static int snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724"); + strscpy(pcm->name, "ICE1724"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1313,7 +1313,7 @@ static int snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1425,7 +1425,7 @@ static int snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device) pcm->private_data = ice; pcm->info_flags = 0; - strcpy(pcm->name, "ICE1724 Surround PCM"); + strscpy(pcm->name, "ICE1724 Surround PCM"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &ice->pci->dev, 256*1024, 256*1024); @@ -1820,7 +1820,7 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; if (uinfo->value.enumerated.item >= hw_rates_count) /* ext_clock items */ - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ice->ext_clock_names[ uinfo->value.enumerated.item - hw_rates_count]); else @@ -2545,8 +2545,8 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice = card->private_data; - strcpy(card->driver, "ICE1724"); - strcpy(card->shortname, "ICEnsemble ICE1724"); + strscpy(card->driver, "ICE1724"); + strscpy(card->shortname, "ICEnsemble ICE1724"); err = snd_vt1724_create(card, pci, model[dev]); if (err < 0) @@ -2557,9 +2557,9 @@ static int __snd_vt1724_probe(struct pci_dev *pci, c = ice->card_info; if (c) { - strcpy(card->shortname, c->name); + strscpy(card->shortname, c->name); if (c->driver) /* specific driver? */ - strcpy(card->driver, c->driver); + strscpy(card->driver, c->driver); if (c->chip_init) { err = c->chip_init(ice); if (err < 0) @@ -2637,7 +2637,7 @@ static int __snd_vt1724_probe(struct pci_dev *pci, return err; ice->rmidi[0] = rmidi; rmidi->private_data = ice; - strcpy(rmidi->name, "ICE1724 MIDI"); + strscpy(rmidi->name, "ICE1724 MIDI"); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; -- GitLab From 9a86ffe73b22260bd25274a6fb0579f77f20342a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:50 +0200 Subject: [PATCH 596/868] ALSA: intel8x0: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-69-tiwai@suse.de --- sound/pci/intel8x0.c | 14 +++++++------- sound/pci/intel8x0m.c | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index b521cec203336..9e6a5065ffbf5 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1436,7 +1436,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -1453,7 +1453,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip), @@ -3118,21 +3118,21 @@ static int __snd_intel8x0_probe(struct pci_dev *pci, if (spdif_aclink < 0) spdif_aclink = check_default_spdif_aclink(pci); - strcpy(card->driver, "ICH"); + strscpy(card->driver, "ICH"); if (!spdif_aclink) { switch (pci_id->driver_data) { case DEVICE_NFORCE: - strcpy(card->driver, "NFORCE"); + strscpy(card->driver, "NFORCE"); break; case DEVICE_INTEL_ICH4: - strcpy(card->driver, "ICH4"); + strscpy(card->driver, "ICH4"); } } - strcpy(card->shortname, "Intel ICH"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 1ce775fe8a70c..9e5988583abe9 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -678,7 +678,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(name, "Intel ICH - %s", rec->suffix); else - strcpy(name, "Intel ICH"); + strscpy(name, "Intel ICH"); err = snd_pcm_new(chip->card, name, device, rec->playback_ops ? 1 : 0, rec->capture_ops ? 1 : 0, &pcm); @@ -696,7 +696,7 @@ static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, if (rec->suffix) sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix); else - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1184,11 +1184,11 @@ static int __snd_intel8x0m_probe(struct pci_dev *pci, return err; chip = card->private_data; - strcpy(card->driver, "ICH-MODEM"); - strcpy(card->shortname, "Intel ICH"); + strscpy(card->driver, "ICH-MODEM"); + strscpy(card->shortname, "Intel ICH"); for (name = shortnames; name->id; name++) { if (pci->device == name->id) { - strcpy(card->shortname, name->s); + strscpy(card->shortname, name->s); break; } } -- GitLab From 22b331f5a2a0e6534214b38c58d806d2aa43ae82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:51 +0200 Subject: [PATCH 597/868] ALSA: korg1212: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-70-tiwai@suse.de --- sound/pci/korg1212/korg1212.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 56dea5b10828a..0a66d5cfc090e 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2249,7 +2249,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->pcm->private_data = korg1212; korg1212->pcm->private_free = snd_korg1212_free_pcm; - strcpy(korg1212->pcm->name, "korg1212"); + strscpy(korg1212->pcm->name, "korg1212"); snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops); @@ -2298,8 +2298,8 @@ snd_korg1212_probe(struct pci_dev *pci, if (err < 0) goto error; - strcpy(card->driver, "korg1212"); - strcpy(card->shortname, "korg1212"); + strscpy(card->driver, "korg1212"); + strscpy(card->shortname, "korg1212"); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, korg1212->iomem, korg1212->irq); -- GitLab From cc519d221f9c8f1ff574b7fec0d875fb76a436b8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:52 +0200 Subject: [PATCH 598/868] ALSA: lola: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-71-tiwai@suse.de --- sound/pci/lola/lola.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index fb8bd54e4c2de..8d927ecba165b 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -630,12 +630,12 @@ static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev) if (err < 0) return err; - strcpy(card->driver, "Lola"); + strscpy(card->driver, "Lola"); strscpy(card->shortname, "Digigram Lola", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, chip->bar[0].addr, chip->irq); - strcpy(card->mixername, card->shortname); + strscpy(card->mixername, card->shortname); lola_irq_enable(chip); -- GitLab From ec4894f82775c9abe14afef9a29b913ee549ec06 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:53 +0200 Subject: [PATCH 599/868] ALSA: lx6464es: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-72-tiwai@suse.de --- sound/pci/lx6464es/lx6464es.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 63ebf9803ea80..9f12c936bb1f7 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -814,7 +814,7 @@ static int lx_pcm_create(struct lx6464es *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, card_name); + strscpy(pcm->name, card_name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, size, size); @@ -1022,7 +1022,7 @@ static int snd_lx6464es_probe(struct pci_dev *pci, goto error; } - strcpy(card->driver, "LX6464ES"); + strscpy(card->driver, "LX6464ES"); sprintf(card->id, "LX6464ES_%02X%02X%02X", chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]); -- GitLab From c1b4f94c7645c58111cf53eb5165ab5fc65ec67d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:54 +0200 Subject: [PATCH 600/868] ALSA: maestro3: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-73-tiwai@suse.de --- sound/pci/maestro3.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index e61e157747069..e092097599ff9 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1846,7 +1846,7 @@ snd_m3_pcm(struct snd_m3 * chip, int device) pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, chip->card->driver); + strscpy(pcm->name, chip->card->driver); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -2648,14 +2648,14 @@ __snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: case PCI_DEVICE_ID_ESS_ALLEGRO_1: - strcpy(card->driver, "Allegro"); + strscpy(card->driver, "Allegro"); break; case PCI_DEVICE_ID_ESS_CANYON3D_2LE: case PCI_DEVICE_ID_ESS_CANYON3D_2: - strcpy(card->driver, "Canyon3D-2"); + strscpy(card->driver, "Canyon3D-2"); break; default: - strcpy(card->driver, "Maestro3"); + strscpy(card->driver, "Maestro3"); break; } -- GitLab From ed04b49e21f05b0f84bbaa86a59abd3ef64de946 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:55 +0200 Subject: [PATCH 601/868] ALSA: mixart: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-74-tiwai@suse.de --- sound/pci/mixart/mixart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7ceaf6a7a77ea..cdc0ba5dd1adb 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -970,7 +970,7 @@ static int snd_mixart_pcm_analog(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1004,7 +1004,7 @@ static int snd_mixart_pcm_digital(struct snd_mixart *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -1330,7 +1330,7 @@ static int snd_mixart_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, CARD_NAME); + strscpy(card->driver, CARD_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram miXart [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), -- GitLab From 6ffb7be30ba0c064e3b42c3d643a1419b9ccc981 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:56 +0200 Subject: [PATCH 602/868] ALSA: nm256: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-75-tiwai@suse.de --- sound/pci/nm256/nm256.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index cd4dc43dbff1f..39464d171f6bf 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1595,13 +1595,13 @@ static int snd_nm256_probe(struct pci_dev *pci, switch (pci->device) { case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: - strcpy(card->driver, "NM256AV"); + strscpy(card->driver, "NM256AV"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: - strcpy(card->driver, "NM256ZX"); + strscpy(card->driver, "NM256ZX"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: - strcpy(card->driver, "NM256XL+"); + strscpy(card->driver, "NM256XL+"); break; default: dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device); -- GitLab From 1c8e3ebdfe3342b885cdf519d0bbc9e5a494c60a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:57 +0200 Subject: [PATCH 603/868] ALSA: oxygen: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-76-tiwai@suse.de --- sound/pci/oxygen/oxygen_lib.c | 6 +++--- sound/pci/oxygen/oxygen_pcm.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 39b8ccf37cdd1..9c7270e4c35e4 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -655,11 +655,11 @@ static int __oxygen_pci_probe(struct pci_dev *pci, int index, char *id, chip->irq = pci->irq; card->sync_irq = chip->irq; - strcpy(card->driver, chip->model.chip); - strcpy(card->shortname, chip->model.shortname); + strscpy(card->driver, chip->model.chip); + strscpy(card->shortname, chip->model.shortname); sprintf(card->longname, "%s at %#lx, irq %i", chip->model.longname, chip->addr, chip->irq); - strcpy(card->mixername, chip->model.chip); + strscpy(card->mixername, chip->model.chip); snd_component_add(card, chip->model.chip); err = oxygen_pcm_init(chip); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b2a3fcfe31d44..643141f345bba 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -697,7 +697,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, "Multichannel"); + strscpy(pcm->name, "Multichannel"); if (outs) snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, SNDRV_DMA_TYPE_DEV, @@ -725,7 +725,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_c_ops); pcm->private_data = chip; - strcpy(pcm->name, "Digital"); + strscpy(pcm->name, "Digital"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -755,7 +755,7 @@ int oxygen_pcm_init(struct oxygen *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_b_ops); pcm->private_data = chip; - strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); + strscpy(pcm->name, outs ? "Front Panel" : "Analog 2"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, @@ -773,7 +773,7 @@ int oxygen_pcm_init(struct oxygen *chip) OXYGEN_REC_C_ROUTE_I2S_ADC_3, OXYGEN_REC_C_ROUTE_MASK); pcm->private_data = chip; - strcpy(pcm->name, "Analog 3"); + strscpy(pcm->name, "Analog 3"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, DEFAULT_BUFFER_BYTES, -- GitLab From c810473253842bf8df21bd0dee5195ef62d95a3c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:58 +0200 Subject: [PATCH 604/868] ALSA: pcxhr: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-77-tiwai@suse.de --- sound/pci/pcxhr/pcxhr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 242bd7e04b3e1..bfd84c50e9814 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1149,7 +1149,7 @@ int pcxhr_create_pcm(struct snd_pcxhr *chip) pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, name); + strscpy(pcm->name, name); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->mgr->pci->dev, @@ -1605,7 +1605,7 @@ static int pcxhr_probe(struct pci_dev *pci, return err; } - strcpy(card->driver, DRIVER_NAME); + strscpy(card->driver, DRIVER_NAME); snprintf(card->shortname, sizeof(card->shortname), "Digigram [PCM #%d]", i); snprintf(card->longname, sizeof(card->longname), -- GitLab From 9885bd7c4ce531c784cc1ce6b3f07ba84480ff5b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:06:59 +0200 Subject: [PATCH 605/868] ALSA: riptide: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-78-tiwai@suse.de --- sound/pci/riptide/riptide.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 578be0755b8ae..e983cd657e285 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1688,7 +1688,7 @@ static int snd_riptide_pcm(struct snd_riptide *chip, int device) &snd_riptide_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; - strcpy(pcm->name, "RIPTIDE"); + strscpy(pcm->name, "RIPTIDE"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, &chip->pci->dev, 64 * 1024, 128 * 1024); @@ -2098,8 +2098,8 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } #endif - strcpy(card->driver, "RIPTIDE"); - strcpy(card->shortname, "Riptide"); + strscpy(card->driver, "RIPTIDE"); + strscpy(card->shortname, "Riptide"); #ifdef SUPPORT_JOYSTICK scnprintf(card->longname, sizeof(card->longname), "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x", -- GitLab From ca485569ca35c4a5dd4e744ef4d4f9ef9c5a3e1b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:00 +0200 Subject: [PATCH 606/868] ALSA: rme32: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-79-tiwai@suse.de --- sound/pci/rme32.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 4bf122abea487..f07b6023473a0 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1314,7 +1314,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->spdif_pcm->private_data = rme32; rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; - strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); + strscpy(rme32->spdif_pcm->name, "Digi32 IEC958"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_spdif_fd_ops); @@ -1344,7 +1344,7 @@ static int snd_rme32_create(struct rme32 *rme32) return err; rme32->adat_pcm->private_data = rme32; rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; - strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); + strscpy(rme32->adat_pcm->name, "Digi32 ADAT"); if (rme32->fullduplex_mode) { snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme32_playback_adat_fd_ops); @@ -1879,16 +1879,16 @@ __snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (err < 0) return err; - strcpy(card->driver, "Digi32"); + strscpy(card->driver, "Digi32"); switch (rme32->pci->device) { case PCI_DEVICE_ID_RME_DIGI32: - strcpy(card->shortname, "RME Digi32"); + strscpy(card->shortname, "RME Digi32"); break; case PCI_DEVICE_ID_RME_DIGI32_8: - strcpy(card->shortname, "RME Digi32/8"); + strscpy(card->shortname, "RME Digi32/8"); break; case PCI_DEVICE_ID_RME_DIGI32_PRO: - strcpy(card->shortname, "RME Digi32 PRO"); + strscpy(card->shortname, "RME Digi32 PRO"); break; } sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d", -- GitLab From 50301b7a02c341b09d3191c9ea02b3d04bdd2bb8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:01 +0200 Subject: [PATCH 607/868] ALSA: rme96: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-80-tiwai@suse.de --- sound/pci/rme96.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 01029843d7f3d..5cdbbe9cf9941 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1607,7 +1607,7 @@ snd_rme96_create(struct rme96 *rme96) rme96->spdif_pcm->private_data = rme96; rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; - strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); + strscpy(rme96->spdif_pcm->name, "Digi96 IEC958"); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); @@ -1624,7 +1624,7 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->adat_pcm->private_data = rme96; rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; - strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); + strscpy(rme96->adat_pcm->name, "Digi96 ADAT"); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); @@ -2434,23 +2434,23 @@ __snd_rme96_probe(struct pci_dev *pci, return -ENOMEM; } - strcpy(card->driver, "Digi96"); + strscpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: - strcpy(card->shortname, "RME Digi96"); + strscpy(card->shortname, "RME Digi96"); break; case PCI_DEVICE_ID_RME_DIGI96_8: - strcpy(card->shortname, "RME Digi96/8"); + strscpy(card->shortname, "RME Digi96/8"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PRO: - strcpy(card->shortname, "RME Digi96/8 PRO"); + strscpy(card->shortname, "RME Digi96/8 PRO"); break; case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST: pci_read_config_byte(rme96->pci, 8, &val); if (val < 5) { - strcpy(card->shortname, "RME Digi96/8 PAD"); + strscpy(card->shortname, "RME Digi96/8 PAD"); } else { - strcpy(card->shortname, "RME Digi96/8 PST"); + strscpy(card->shortname, "RME Digi96/8 PST"); } break; } -- GitLab From 43b90c3fe5528abb638ebc4a84924c08d636f82f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:02 +0200 Subject: [PATCH 608/868] ALSA: hdsp: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-81-tiwai@suse.de --- sound/pci/rme9652/hdsp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 8df0f5bba0f60..7ce73746168a1 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -4944,7 +4944,7 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) hdsp->hwdep = hw; hw->private_data = hdsp; - strcpy(hw->name, "HDSP hwdep interface"); + strscpy(hw->name, "HDSP hwdep interface"); hw->ops.ioctl = snd_hdsp_hwdep_ioctl; hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl; @@ -4963,7 +4963,7 @@ static int snd_hdsp_create_pcm(struct snd_card *card, struct hdsp *hdsp) hdsp->pcm = pcm; pcm->private_data = hdsp; - strcpy(pcm->name, hdsp->card_name); + strscpy(pcm->name, hdsp->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops); @@ -5111,7 +5111,7 @@ static int snd_hdsp_create_alsa_devices(struct snd_card *card, struct hdsp *hdsp } if (!(hdsp->state & HDSP_InitializationComplete)) { - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); @@ -5255,8 +5255,8 @@ static int snd_hdsp_create(struct snd_card *card, */ pci_write_config_byte(hdsp->pci, PCI_LATENCY_TIMER, 0xFF); - strcpy(card->driver, "H-DSP"); - strcpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "H-DSP"); + strscpy(card->mixername, "Xilinx FPGA"); if (hdsp->firmware_rev < 0xa) return -ENODEV; @@ -5412,7 +5412,7 @@ static int snd_hdsp_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, "Hammerfall DSP"); + strscpy(card->shortname, "Hammerfall DSP"); sprintf(card->longname, "%s at 0x%lx, irq %d", hdsp->card_name, hdsp->port, hdsp->irq); err = snd_card_register(card); -- GitLab From 4a9b01c7e58602a084edcb36bfce0b80f2775691 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:03 +0200 Subject: [PATCH 609/868] ALSA: hdspm: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-82-tiwai@suse.de --- sound/pci/rme9652/hdspm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2041cf00cca0c..a0976824bedaa 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6355,7 +6355,7 @@ static int snd_hdspm_create_hwdep(struct snd_card *card, hdspm->hwdep = hw; hw->private_data = hdspm; - strcpy(hw->name, "HDSPM hwdep interface"); + strscpy(hw->name, "HDSPM hwdep interface"); hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; @@ -6412,7 +6412,7 @@ static int snd_hdspm_create_pcm(struct snd_card *card, hdspm->pcm = pcm; pcm->private_data = hdspm; - strcpy(pcm->name, hdspm->card_name); + strscpy(pcm->name, hdspm->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdspm_ops); @@ -6512,8 +6512,8 @@ static int snd_hdspm_create(struct snd_card *card, pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); - strcpy(card->mixername, "Xilinx FPGA"); - strcpy(card->driver, "HDSPM"); + strscpy(card->mixername, "Xilinx FPGA"); + strscpy(card->driver, "HDSPM"); switch (hdspm->firmware_rev) { case HDSPM_RAYDAT_REV: -- GitLab From a8b1aba400a9348d474ffc5ba9a34fbd10f2107f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:04 +0200 Subject: [PATCH 610/868] ALSA: rme9652: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-83-tiwai@suse.de --- sound/pci/rme9652/rme9652.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 34d9c7995ddd0..7dc8e3777c37d 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2364,7 +2364,7 @@ static int snd_rme9652_create_pcm(struct snd_card *card, rme9652->pcm = pcm; pcm->private_data = rme9652; - strcpy(pcm->name, rme9652->card_name); + strscpy(pcm->name, rme9652->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme9652_capture_ops); @@ -2447,7 +2447,7 @@ static int snd_rme9652_create(struct snd_card *card, switch (rev) { case 8: /* original eprom */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9636 (Rev 1.5)"; } else { @@ -2456,17 +2456,17 @@ static int snd_rme9652_create(struct snd_card *card, rme9652->ss_channels = RME9636_NCHANNELS; break; case 9: /* W36_G EPROM */ - strcpy(card->driver, "RME9636"); + strscpy(card->driver, "RME9636"); rme9652->card_name = "RME Digi9636 (Rev G)"; rme9652->ss_channels = RME9636_NCHANNELS; break; case 4: /* W52_G EPROM */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); rme9652->card_name = "RME Digi9652 (Rev G)"; rme9652->ss_channels = RME9652_NCHANNELS; break; case 3: /* original eprom */ - strcpy(card->driver, "RME9652"); + strscpy(card->driver, "RME9652"); if (rme9652->hw_rev == 15) { rme9652->card_name = "RME Digi9652 (Rev 1.5)"; } else { @@ -2539,7 +2539,7 @@ static int snd_rme9652_probe(struct pci_dev *pci, if (err) goto error; - strcpy(card->shortname, rme9652->card_name); + strscpy(card->shortname, rme9652->card_name); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, rme9652->port, rme9652->irq); -- GitLab From b097bdf5e92d4bcb5eb9bb76165558d0c11f5cb9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:05 +0200 Subject: [PATCH 611/868] ALSA: sis7019: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-84-tiwai@suse.de --- sound/pci/sis7019.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 42b22f123fa74..3d7abcb316790 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -868,7 +868,7 @@ static int sis_pcm_create(struct sis7019 *sis) return rc; pcm->private_data = sis; - strcpy(pcm->name, "SiS7019"); + strscpy(pcm->name, "SiS7019"); sis->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops); @@ -1348,8 +1348,8 @@ static int __snd_sis7019_probe(struct pci_dev *pci, if (rc < 0) return rc; - strcpy(card->driver, "SiS7019"); - strcpy(card->shortname, "SiS7019"); + strscpy(card->driver, "SiS7019"); + strscpy(card->shortname, "SiS7019"); rc = sis_chip_create(card, pci); if (rc) return rc; -- GitLab From 7ffad83d527e7ab3ac33df47e93dd94eccffc15f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:06 +0200 Subject: [PATCH 612/868] ALSA: sonicvibes: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-85-tiwai@suse.de --- sound/pci/sonicvibes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 808a793ff4da6..f85a9556dacba 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -863,7 +863,7 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device) pcm->private_data = sonic; pcm->info_flags = 0; - strcpy(pcm->name, "S3 SonicVibes"); + strscpy(pcm->name, "S3 SonicVibes"); sonic->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1091,7 +1091,7 @@ static int snd_sonicvibes_mixer(struct sonicvibes *sonic) if (snd_BUG_ON(!sonic || !sonic->card)) return -EINVAL; card = sonic->card; - strcpy(card->mixername, "S3 SonicVibes"); + strscpy(card->mixername, "S3 SonicVibes"); for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) { kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic); @@ -1415,8 +1415,8 @@ static int __snd_sonic_probe(struct pci_dev *pci, if (err < 0) return err; - strcpy(card->driver, "SonicVibes"); - strcpy(card->shortname, "S3 SonicVibes"); + strscpy(card->driver, "SonicVibes"); + strscpy(card->shortname, "S3 SonicVibes"); sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", card->shortname, sonic->revision, -- GitLab From b28309eac3b755d9021544960e6f6ea0507678fd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:07 +0200 Subject: [PATCH 613/868] ALSA: trident: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-86-tiwai@suse.de --- sound/pci/trident/trident.c | 6 +++--- sound/pci/trident/trident_main.c | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 9922ab40798cf..ddb6ccc72e443 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -88,11 +88,11 @@ static int snd_trident_probe(struct pci_dev *pci, default: str = "Unknown"; } - strcpy(card->driver, str); + strscpy(card->driver, str); if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - strcpy(card->shortname, "SiS "); + strscpy(card->shortname, "SiS "); } else { - strcpy(card->shortname, "Trident "); + strscpy(card->shortname, "Trident "); } strcat(card->shortname, str); sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 4e16b79d6584b..39ed52bf8631c 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2137,7 +2137,7 @@ int snd_trident_pcm(struct snd_trident *trident, int device) pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Trident 4DWave"); + strscpy(pcm->name, "Trident 4DWave"); trident->pcm = pcm; if (trident->tlb.entries) { @@ -2189,16 +2189,16 @@ int snd_trident_foldback_pcm(struct snd_trident *trident, int device) else snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); foldback->info_flags = 0; - strcpy(foldback->name, "Trident 4DWave"); + strscpy(foldback->name, "Trident 4DWave"); substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - strcpy(substream->name, "Front Mixer"); + strscpy(substream->name, "Front Mixer"); substream = substream->next; - strcpy(substream->name, "Reverb Mixer"); + strscpy(substream->name, "Reverb Mixer"); substream = substream->next; - strcpy(substream->name, "Chorus Mixer"); + strscpy(substream->name, "Chorus Mixer"); if (num_chan == 4) { substream = substream->next; - strcpy(substream->name, "Second AC'97 ADC"); + strscpy(substream->name, "Second AC'97 ADC"); } trident->foldback = foldback; @@ -2241,7 +2241,7 @@ int snd_trident_spdif_pcm(struct snd_trident *trident, int device) snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); } spdif->info_flags = 0; - strcpy(spdif->name, "Trident 4DWave IEC958"); + strscpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; snd_pcm_set_managed_buffer_all(spdif, SNDRV_DMA_TYPE_DEV, -- GitLab From fd86b9bbf5663b8f4752bbf780be55fd8437bf00 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:08 +0200 Subject: [PATCH 614/868] ALSA: via82xx: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-87-tiwai@suse.de --- sound/pci/via82xx.c | 20 ++++++++++---------- sound/pci/via82xx_modem.c | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a04dbc0a420f6..0753c0c73f514 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1436,7 +1436,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playbacks */ for (i = 0; i < 4; i++) @@ -1461,7 +1461,7 @@ static int snd_via8233_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1504,7 +1504,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; /* set up playback */ init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); @@ -1532,7 +1532,7 @@ static int snd_via8233a_pcm_new(struct via82xx *chip) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[1] = pcm; /* set up playback */ init_viadev(chip, chip->playback_devno, 0x30, 3, 0); @@ -1562,7 +1562,7 @@ static int snd_via686_pcm_new(struct via82xx *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0); init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1); @@ -2461,7 +2461,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA686: - strcpy(card->driver, "VIA686A"); + strscpy(card->driver, "VIA686A"); sprintf(card->shortname, "VIA 82C686A/B rev%x", pci->revision); chip_type = TYPE_VIA686; break; @@ -2471,7 +2471,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) { if (pci->revision == via823x_cards[i].revision) { chip_type = via823x_cards[i].type; - strcpy(card->shortname, via823x_cards[i].name); + strscpy(card->shortname, via823x_cards[i].name); break; } } @@ -2487,11 +2487,11 @@ static int __snd_via82xx_probe(struct pci_dev *pci, chip_type = TYPE_VIA8233; } if (chip_type == TYPE_VIA8233A) - strcpy(card->driver, "VIA8233A"); + strscpy(card->driver, "VIA8233A"); else if (pci->revision >= VIA_REV_8237) - strcpy(card->driver, "VIA8237"); /* no slog assignment */ + strscpy(card->driver, "VIA8237"); /* no slog assignment */ else - strcpy(card->driver, "VIA8233"); + strscpy(card->driver, "VIA8233"); break; default: dev_err(card->dev, "invalid card type %d\n", card_type); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index eef0f9ddaae07..12a8c620724d2 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -842,7 +842,7 @@ static int snd_via686_pcm_new(struct via82xx_modem *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); pcm->dev_class = SNDRV_PCM_CLASS_MODEM; pcm->private_data = chip; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcms[0] = pcm; init_viadev(chip, 0, VIA_REG_MO_STATUS, 0); init_viadev(chip, 1, VIA_REG_MI_STATUS, 1); @@ -1116,7 +1116,7 @@ static int __snd_via82xx_probe(struct pci_dev *pci, card_type = pci_id->driver_data; switch (card_type) { case TYPE_CARD_VIA82XX_MODEM: - strcpy(card->driver, "VIA82XX-MODEM"); + strscpy(card->driver, "VIA82XX-MODEM"); sprintf(card->shortname, "VIA 82XX modem"); break; default: -- GitLab From 362c6bbe367c769ef4526c4a2d3cebfcbc58d0ee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:09 +0200 Subject: [PATCH 615/868] ALSA: ymfpci: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-88-tiwai@suse.de --- sound/pci/ymfpci/ymfpci.c | 2 +- sound/pci/ymfpci/ymfpci_main.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 48444dda44def..764ca59e98d1d 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -188,7 +188,7 @@ static int __snd_card_ymfpci_probe(struct pci_dev *pci, default: model = str = "???"; break; } - strcpy(card->driver, str); + strscpy(card->driver, str); sprintf(card->shortname, "Yamaha %s (%s)", model, str); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index d495f53a8324e..75e013b66c5b3 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1134,7 +1134,7 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI"); + strscpy(pcm->name, "YMFPCI"); chip->pcm = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1201,7 +1201,7 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - IEC958"); + strscpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1242,7 +1242,7 @@ int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device) /* global setup */ pcm->info_flags = 0; - strcpy(pcm->name, "YMFPCI - Rear PCM"); + strscpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, @@ -1948,7 +1948,7 @@ int snd_ymfpci_timer(struct snd_ymfpci *chip, int device) tid.subdevice = 0; err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer); if (err >= 0) { - strcpy(timer->name, "YMFPCI timer"); + strscpy(timer->name, "YMFPCI timer"); timer->private_data = chip; timer->hw = snd_ymfpci_timer_hw; } -- GitLab From a5546578af79da589e27d50441ef6881de21e65d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:10 +0200 Subject: [PATCH 616/868] ALSA: pdaudiocf: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-89-tiwai@suse.de --- sound/pcmcia/pdaudiocf/pdaudiocf.c | 2 +- sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index 4944607466146..13419837dfb7c 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -160,7 +160,7 @@ static int snd_pdacf_assign_resources(struct snd_pdacf *pdacf, int port, int irq if (err < 0) return err; - strcpy(card->driver, "PDAudio-CF"); + strscpy(card->driver, "PDAudio-CF"); sprintf(card->shortname, "Core Sound %s", card->driver); sprintf(card->longname, "%s at 0x%x, irq %i", card->shortname, port, irq); diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c index aaa82ec36540d..20aba745f1dcd 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c @@ -263,7 +263,7 @@ int snd_pdacf_pcm_new(struct snd_pdacf *chip) pcm->private_data = chip; pcm->info_flags = 0; pcm->nonatomic = true; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); -- GitLab From 704a54b84f567ecbefdce250f455c9ff2d4f3860 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:11 +0200 Subject: [PATCH 617/868] ALSA: vxpocket: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-90-tiwai@suse.de --- sound/pcmcia/vx/vxpocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index d2d5f64d63b43..2e09f2a513a6a 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -187,7 +187,7 @@ static int vxpocket_config(struct pcmcia_device *link) /* overwrite the hardware information */ chip->hw = &vxp440_hw; chip->type = vxp440_hw.type; - strcpy(chip->card->driver, vxp440_hw.name); + strscpy(chip->card->driver, vxp440_hw.name); } ret = pcmcia_request_io(link); -- GitLab From 292e4adb954b8584b74bff0d1f9044af297fa248 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:12 +0200 Subject: [PATCH 618/868] ALSA: ppc: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-91-tiwai@suse.de --- sound/ppc/awacs.c | 2 +- sound/ppc/burgundy.c | 2 +- sound/ppc/daca.c | 2 +- sound/ppc/pmac.c | 2 +- sound/ppc/powermac.c | 8 ++++---- sound/ppc/snd_ps3.c | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 49399a4a290d7..13a6f3af13ef8 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -956,7 +956,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip) /* * build mixers */ - strcpy(chip->card->mixername, "PowerMac AWACS"); + strscpy(chip->card->mixername, "PowerMac AWACS"); err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), snd_pmac_awacs_mixers); diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 400a886562b11..ba15bc34c9eca 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -665,7 +665,7 @@ int snd_pmac_burgundy_init(struct snd_pmac *chip) /* * build burgundy mixers */ - strcpy(chip->card->mixername, "PowerMac Burgundy"); + strscpy(chip->card->mixername, "PowerMac Burgundy"); for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { err = snd_ctl_add(chip->card, diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index d5766952f3dbe..a74114225b67f 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -261,7 +261,7 @@ int snd_pmac_daca_init(struct snd_pmac *chip) /* * build mixers */ - strcpy(chip->card->mixername, "PowerMac DACA"); + strscpy(chip->card->mixername, "PowerMac DACA"); for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) { err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip)); diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 76674c43fa7eb..a3d346f1cc058 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -679,7 +679,7 @@ int snd_pmac_pcm_new(struct snd_pmac *chip) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, chip->card->shortname); + strscpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index f1b0cf9ea5555..e685d245883ec 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -55,8 +55,8 @@ static int snd_pmac_probe(struct platform_device *devptr) switch (chip->model) { case PMAC_BURGUNDY: - strcpy(card->driver, "PMac Burgundy"); - strcpy(card->shortname, "PowerMac Burgundy"); + strscpy(card->driver, "PMac Burgundy"); + strscpy(card->shortname, "PowerMac Burgundy"); sprintf(card->longname, "%s (Dev %d) Sub-frame %d", card->shortname, chip->device_id, chip->subframe); err = snd_pmac_burgundy_init(chip); @@ -64,8 +64,8 @@ static int snd_pmac_probe(struct platform_device *devptr) goto __error; break; case PMAC_DACA: - strcpy(card->driver, "PMac DACA"); - strcpy(card->shortname, "PowerMac DACA"); + strscpy(card->driver, "PMac DACA"); + strscpy(card->shortname, "PowerMac DACA"); sprintf(card->longname, "%s (Dev %d) Sub-frame %d", card->shortname, chip->device_id, chip->subframe); err = snd_pmac_daca_init(chip); diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index a6cff2c46ac7e..ce7ee2713f9d9 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -951,9 +951,9 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) if (ret < 0) goto clean_irq; - strcpy(the_card.card->driver, "PS3"); - strcpy(the_card.card->shortname, "PS3"); - strcpy(the_card.card->longname, "PS3 sound"); + strscpy(the_card.card->driver, "PS3"); + strscpy(the_card.card->shortname, "PS3"); + strscpy(the_card.card->longname, "PS3 sound"); /* create control elements */ for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { @@ -975,7 +975,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) goto clean_card; the_card.pcm->private_data = &the_card; - strcpy(the_card.pcm->name, "SPDIF"); + strscpy(the_card.pcm->name, "SPDIF"); /* set pcm ops */ snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, -- GitLab From 61d4db8f7c6c63fd4dfba67bda86754770eed627 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:13 +0200 Subject: [PATCH 619/868] ALSA: sh: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-92-tiwai@suse.de --- sound/sh/aica.c | 8 ++++---- sound/sh/sh_dac_audio.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 40ea843113a74..fa81bfba59c14 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -424,7 +424,7 @@ static int __init snd_aicapcmchip(struct snd_card_aica if (unlikely(err < 0)) return err; pcm->private_data = dreamcastcard; - strcpy(pcm->name, "AICA PCM"); + strscpy(pcm->name, "AICA PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_aicapcm_playback_ops); /* Allocate the DMA buffers */ @@ -568,9 +568,9 @@ static int snd_aica_probe(struct platform_device *devptr) kfree(dreamcastcard); return err; } - strcpy(dreamcastcard->card->driver, "snd_aica"); - strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); - strcpy(dreamcastcard->card->longname, + strscpy(dreamcastcard->card->driver, "snd_aica"); + strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); + strscpy(dreamcastcard->card->longname, "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); /* Prepare to use the queue */ INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 84a4b17a0cc23..164f91240d020 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -224,7 +224,7 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device) return err; pcm->private_data = chip; - strcpy(pcm->name, "SH_DAC PCM"); + strscpy(pcm->name, "SH_DAC PCM"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops); /* buffer size=48K */ @@ -358,8 +358,8 @@ static int snd_sh_dac_probe(struct platform_device *devptr) if (err < 0) goto probe_error; - strcpy(card->driver, "snd_sh_dac"); - strcpy(card->shortname, "SuperH DAC audio driver"); + strscpy(card->driver, "snd_sh_dac"); + strscpy(card->shortname, "SuperH DAC audio driver"); dev_info(&devptr->dev, "%s %s\n", card->longname, card->shortname); err = snd_card_register(card); -- GitLab From fee48aed6b8d87667128b2c937fcedecc7da7c1c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:14 +0200 Subject: [PATCH 620/868] ALSA: sparc: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-93-tiwai@suse.de --- sound/sparc/cs4231.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 1b44119edfbc4..1a1fe3ceb76c9 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1230,7 +1230,7 @@ static int snd_cs4231_pcm(struct snd_card *card) /* global setup */ pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - strcpy(pcm->name, "CS4231"); + strscpy(pcm->name, "CS4231"); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->op->dev, 64 * 1024, 128 * 1024); @@ -1256,7 +1256,7 @@ static int snd_cs4231_timer(struct snd_card *card) err = snd_timer_new(card, "CS4231", &tid, &timer); if (err < 0) return err; - strcpy(timer->name, "CS4231"); + strscpy(timer->name, "CS4231"); timer->private_data = chip; timer->hw = snd_cs4231_timer_table; chip->timer = timer; @@ -1530,7 +1530,7 @@ static int snd_cs4231_mixer(struct snd_card *card) if (snd_BUG_ON(!chip || !chip->pcm)) return -EINVAL; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { err = snd_ctl_add(card, @@ -1565,8 +1565,8 @@ static int cs4231_attach_begin(struct platform_device *op, if (err < 0) return err; - strcpy(card->driver, "CS4231"); - strcpy(card->shortname, "Sun CS4231"); + strscpy(card->driver, "CS4231"); + strscpy(card->shortname, "Sun CS4231"); chip = card->private_data; chip->card = card; @@ -1964,12 +1964,12 @@ static int snd_cs4231_ebus_create(struct snd_card *card, chip->op = op; memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); - strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); + strscpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback; chip->c_dma.ebus_info.client_cookie = chip; chip->c_dma.ebus_info.irq = op->archdata.irqs[0]; - strcpy(chip->p_dma.ebus_info.name, "cs4231(play)"); + strscpy(chip->p_dma.ebus_info.name, "cs4231(play)"); chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback; chip->p_dma.ebus_info.client_cookie = chip; -- GitLab From 3fb167d7ceb7f22462743df93d9ed7f06eeaf6d0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:15 +0200 Subject: [PATCH 621/868] ALSA: spi: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-94-tiwai@suse.de --- sound/spi/at73c213.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 8f6929ced2c8a..a0a7f90b61469 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -336,7 +336,7 @@ static int snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; - strcpy(pcm->name, "at73c213"); + strscpy(pcm->name, "at73c213"); chip->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops); @@ -713,7 +713,7 @@ static int snd_at73c213_mixer(struct snd_at73c213 *chip) card = chip->card; - strcpy(card->mixername, chip->pcm->name); + strscpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { errval = snd_ctl_add(card, @@ -983,8 +983,8 @@ static int snd_at73c213_probe(struct spi_device *spi) if (retval) goto out_ssc; - strcpy(card->driver, "at73c213"); - strcpy(card->shortname, board->shortname); + strscpy(card->driver, "at73c213"); + strscpy(card->shortname, board->shortname); sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq); retval = snd_card_register(card); -- GitLab From 2173cee16c2cad741d14c2eb17d39928f7b162c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:16 +0200 Subject: [PATCH 622/868] ALSA: synth: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-95-tiwai@suse.de --- sound/synth/emux/emux_hwdep.c | 2 +- sound/synth/emux/emux_oss.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index 9e98c4c73fd1e..14b990cf3b223 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -121,7 +121,7 @@ snd_emux_init_hwdep(struct snd_emux *emu) if (err < 0) return err; emu->hwdep = hw; - strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME); + strscpy(hw->name, SNDRV_EMUX_HWDEP_NAME); hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE; hw->ops.ioctl = snd_emux_hwdep_ioctl; /* The ioctl parameter types are compatible between 32- and diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index 5f98d5201ba25..1145c7956afde 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -60,7 +60,7 @@ snd_emux_init_seq_oss(struct snd_emux *emu) return; emu->oss_synth = dev; - strcpy(dev->name, emu->name); + strscpy(dev->name, emu->name); arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); arg->type = SYNTH_TYPE_SAMPLE; arg->subtype = SAMPLE_TYPE_AWE32; -- GitLab From 19a28b8c7f9f63019fa1b6ccfbbe78f5fd4bc77c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:17 +0200 Subject: [PATCH 623/868] ALSA: 6fire: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-96-tiwai@suse.de --- sound/usb/6fire/chip.c | 4 ++-- sound/usb/6fire/midi.c | 2 +- sound/usb/6fire/pcm.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index d562a30b087f0..9eb4bf9b138b7 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -125,8 +125,8 @@ static int usb6fire_chip_probe(struct usb_interface *intf, dev_err(&intf->dev, "cannot create alsa card.\n"); return ret; } - strcpy(card->driver, "6FireUSB"); - strcpy(card->shortname, "TerraTec DMX6FireUSB"); + strscpy(card->driver, "6FireUSB"); + strscpy(card->shortname, "TerraTec DMX6FireUSB"); sprintf(card->longname, "%s at %d:%d", card->shortname, device->bus->busnum, device->devnum); diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index de2691d58de6e..923f7767e62fc 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -183,7 +183,7 @@ int usb6fire_midi_init(struct sfire_chip *chip) return ret; } rt->instance->private_data = rt; - strcpy(rt->instance->name, "DMX6FireUSB MIDI"); + strscpy(rt->instance->name, "DMX6FireUSB MIDI"); rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 32c39d8bd2e55..d53cad97889af 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -640,7 +640,7 @@ int usb6fire_pcm_init(struct sfire_chip *chip) } pcm->private_data = rt; - strcpy(pcm->name, "DMX 6Fire USB"); + strscpy(pcm->name, "DMX 6Fire USB"); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); -- GitLab From 2d41b6f40637210a6f41642cfb343d339fa95b52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:18 +0200 Subject: [PATCH 624/868] ALSA: line6: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-97-tiwai@suse.de --- sound/usb/line6/driver.c | 8 ++++---- sound/usb/line6/midi.c | 4 ++-- sound/usb/line6/pcm.c | 2 +- sound/usb/line6/toneport.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index e9eb5c74d6c7a..f2f9261489a21 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -678,7 +678,7 @@ static int line6_hwdep_init(struct usb_line6 *line6) err = snd_hwdep_new(line6->card, "config", 0, &hwdep); if (err < 0) goto end; - strcpy(hwdep->name, "config"); + strscpy(hwdep->name, "config"); hwdep->iface = SNDRV_HWDEP_IFACE_LINE6; hwdep->ops = hwdep_ops; hwdep->private_data = line6; @@ -770,9 +770,9 @@ int line6_probe(struct usb_interface *interface, line6->ifcdev = &interface->dev; INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work); - strcpy(card->id, properties->id); - strcpy(card->driver, driver_name); - strcpy(card->shortname, properties->name); + strscpy(card->id, properties->id); + strscpy(card->driver, driver_name); + strscpy(card->shortname, properties->name); sprintf(card->longname, "Line 6 %s at USB %s", properties->name, dev_name(line6->ifcdev)); card->private_free = line6_destruct; diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index 9b51760862809..1d77794b4490b 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -228,8 +228,8 @@ static int snd_line6_new_midi(struct usb_line6 *line6, return err; rmidi = *rmidi_ret; - strcpy(rmidi->id, line6->properties->id); - strcpy(rmidi->name, line6->properties->name); + strscpy(rmidi->id, line6->properties->id); + strscpy(rmidi->name, line6->properties->name); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index d4dbbc432505d..c1e2a8ab66fac 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -486,7 +486,7 @@ static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) if (err < 0) return err; pcm = *pcm_ret; - strcpy(pcm->name, line6->properties->name); + strscpy(pcm->name, line6->properties->name); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index c073b38cd6738..68cda7bf330c7 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -199,7 +199,7 @@ static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= size) uinfo->value.enumerated.item = size - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, toneport_source_info[uinfo->value.enumerated.item].name); return 0; -- GitLab From f3d81c058f4878f31bbf9dcb33b0acc41d02675c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:19 +0200 Subject: [PATCH 625/868] ALSA: usx2y: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-98-tiwai@suse.de --- sound/usb/usx2y/us122l.c | 2 +- sound/usb/usx2y/usX2Yhwdep.c | 2 +- sound/usb/usx2y/usbusx2y.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index 6bcf8b859ebb2..2ace3ba46091f 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -495,7 +495,7 @@ static int usx2y_create_card(struct usb_device *device, init_waitqueue_head(&US122L(card)->sk.sleep); US122L(card)->is_us144 = flags & US122L_FLAG_US144; INIT_LIST_HEAD(&US122L(card)->midi_list); - strcpy(card->driver, "USB "NAME_ALLCAPS""); + strscpy(card->driver, "USB "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", card->shortname, diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index 9fd6a86cc08e0..4d79251848261 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -102,7 +102,7 @@ static int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw, } if (id < 0) return -ENODEV; - strcpy(info->id, type_ids[id]); + strscpy(info->id, type_ids[id]); info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code if (us428->chip_status & USX2Y_STAT_CHIP_INIT) info->chip_ready = 1; diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 5756ff3528a2d..f34e78910200a 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -382,7 +382,7 @@ static int usx2y_create_card(struct usb_device *device, init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head); mutex_init(&usx2y(card)->pcm_mutex); INIT_LIST_HEAD(&usx2y(card)->midi_list); - strcpy(card->driver, "USB "NAME_ALLCAPS""); + strscpy(card->driver, "USB "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", card->shortname, -- GitLab From 414e4f01f663d9d46cee98d1c8d2ef33f10ec26e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:20 +0200 Subject: [PATCH 626/868] ALSA: ua101: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-99-tiwai@suse.de --- sound/usb/misc/ua101.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 4f6b20ed29dd7..e200e4af799c2 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1246,8 +1246,8 @@ static int ua101_probe(struct usb_interface *interface, goto probe_error; name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101"; - strcpy(card->driver, "UA-101"); - strcpy(card->shortname, name); + strscpy(card->driver, "UA-101"); + strscpy(card->shortname, name); usb_make_path(ua->dev, usb_path, sizeof(usb_path)); snprintf(ua->card->longname, sizeof(ua->card->longname), "EDIROL %s (serial %s), %u Hz at %s, %s speed", name, @@ -1272,7 +1272,7 @@ static int ua101_probe(struct usb_interface *interface, if (err < 0) goto probe_error; ua->pcm->private_data = ua; - strcpy(ua->pcm->name, name); + strscpy(ua->pcm->name, name); snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops); snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops); snd_pcm_set_managed_buffer_all(ua->pcm, SNDRV_DMA_TYPE_VMALLOC, -- GitLab From a9b25e8a437586fb1e1749caae96e305dc9e4906 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:21 +0200 Subject: [PATCH 627/868] ALSA: usb-audio: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-100-tiwai@suse.de --- sound/usb/card.c | 2 +- sound/usb/midi.c | 2 +- sound/usb/mixer.c | 2 +- sound/usb/stream.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 9fb8726a6c935..630ce40ed2c95 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -756,7 +756,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, card->private_free = snd_usb_audio_free; - strcpy(card->driver, "USB-Audio"); + strscpy(card->driver, "USB-Audio"); sprintf(component, "USB%04x:%04x", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 866e613fee4f1..acb3bf92857c1 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2407,7 +2407,7 @@ static int snd_usbmidi_create_rawmidi(struct snd_usb_midi *umidi, out_ports, in_ports, &rmidi); if (err < 0) return err; - strcpy(rmidi->name, umidi->card->shortname); + strscpy(rmidi->name, umidi->card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 11be79af26db6..a7cd7d51e66b5 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3569,7 +3569,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif) struct usb_mixer_interface *mixer; int err; - strcpy(chip->card->mixername, "USB Mixer"); + strscpy(chip->card->mixername, "USB Mixer"); mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); if (!mixer) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index aa91d63749f2c..749498fbf9cb2 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -538,7 +538,7 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, if (chip->pcm_devs > 0) sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); else - strcpy(pcm->name, "USB Audio"); + strscpy(pcm->name, "USB Audio"); snd_usb_init_substream(as, stream, fp, pd); -- GitLab From 926359588170882ab39c578774a989a5237a3747 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:22 +0200 Subject: [PATCH 628/868] ALSA: ac97: Copy string more safely snd_ac97_get_name() blindly assumes that the name buffer is large enough, but we should be more careful. Pass the max buffer length and allow trimming to the size. Only a cosmetic safety matter, no functional changes intended. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-101-tiwai@suse.de --- sound/pci/ac97/ac97_codec.c | 19 +++++++++++-------- sound/pci/ac97/ac97_local.h | 2 +- sound/pci/ac97/ac97_proc.c | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 588c094080bea..cd60c856a92e9 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1840,7 +1840,8 @@ static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id return NULL; } -void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem) +void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, + size_t maxlen, int modem) { const struct ac97_codec_id *pid; @@ -1852,7 +1853,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m if (! pid) return; - strcpy(name, pid->name); + strscpy(name, pid->name, maxlen); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) @@ -1861,8 +1862,8 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m pid = look_for_codec_id(snd_ac97_codec_ids, id); if (pid) { - strcat(name, " "); - strcat(name, pid->name); + strlcat(name, " ", maxlen); + strlcat(name, pid->name, maxlen); if (pid->mask != 0xffffffff) sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { @@ -1870,8 +1871,10 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m (! modem && ! (pid->flags & AC97_MODEM_PATCH))) pid->patch(ac97); } - } else - sprintf(name + strlen(name), " id %x", id & 0xff); + } else { + int l = strlen(name); + snprintf(name + l, maxlen - l, " id %x", id & 0xff); + } } /** @@ -2295,8 +2298,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, /* additional initializations */ if (bus->ops->init) bus->ops->init(ac97); - snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97)); - snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code + snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code if (! ac97->build_ops) ac97->build_ops = &null_build_ops; diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h index 8eeae2dec552a..965284eb4b336 100644 --- a/sound/pci/ac97/ac97_local.h +++ b/sound/pci/ac97/ac97_local.h @@ -8,7 +8,7 @@ */ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, - int modem); + size_t maxlen, int modem); int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 518834964b7b4..2df3ba9a08dc5 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -98,7 +98,7 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" }; static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" }; - snd_ac97_get_name(NULL, ac97->id, name, 0); + snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), 0); snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name); if ((ac97->scaps & AC97_SCAP_AUDIO) == 0) -- GitLab From f15be4dca2a622fa397eae43f03e71e68e50a266 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:23 +0200 Subject: [PATCH 629/868] ALSA: cmipci: Copy string more safely The probe code uses sprintf() and strcat() without caring about the string buffer size. Replace with safer code. Only a cosmetic safety matter, no functional changes intended. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-102-tiwai@suse.de --- sound/pci/cmipci.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 9056214e9cdae..c4ee550d7c96c 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3008,11 +3008,12 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B) query_chip(cm); /* added -MCx suffix for chip supporting multi-channels */ - if (cm->can_multi_ch) - sprintf(cm->card->driver + strlen(cm->card->driver), - "-MC%d", cm->max_channels); - else if (cm->can_ac3_sw) - strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC"); + if (cm->can_multi_ch) { + int l = strlen(cm->card->driver); + scnprintf(cm->card->driver + l, sizeof(cm->card->driver) - l, + "-MC%d", cm->max_channels); + } else if (cm->can_ac3_sw) + strlcat(cm->card->driver, "-SWIEC", sizeof(cm->card->driver)); cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; -- GitLab From 53beb4d0ed8f9bf31ba30e65953abb473b191bd0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:24 +0200 Subject: [PATCH 630/868] ALSA: usb-audio: Copy string more safely Replace strcpy() and sprintf() usages in the USB audio drivers with the safer versions (strscpy() and scnprintf()) with the proper max size evaluation. Only for safety, no actual behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-103-tiwai@suse.de --- sound/usb/card.c | 11 ++++++----- sound/usb/midi2.c | 3 ++- sound/usb/mixer.c | 22 +++++++++++----------- sound/usb/mixer_scarlett.c | 31 ++++++++++++++++++------------- sound/usb/mixer_scarlett2.c | 14 ++++++++------ sound/usb/proc.c | 2 +- sound/usb/stream.c | 3 ++- 7 files changed, 48 insertions(+), 38 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 630ce40ed2c95..10d9b72855970 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -616,9 +616,10 @@ static void usb_audio_make_shortname(struct usb_device *dev, usb_string(dev, dev->descriptor.iProduct, card->shortname, sizeof(card->shortname)) <= 0) { /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); + scnprintf(card->shortname, sizeof(card->shortname), + "USB Device %#04x:%#04x", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); } strim(card->shortname); @@ -757,8 +758,8 @@ static int snd_usb_audio_create(struct usb_interface *intf, card->private_free = snd_usb_audio_free; strscpy(card->driver, "USB-Audio"); - sprintf(component, "USB%04x:%04x", - USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); + scnprintf(component, sizeof(component), "USB%04x:%04x", + USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); usb_audio_make_shortname(dev, chip, quirk); diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 692dfc3c182f0..030569fda416f 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -1058,7 +1058,8 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi) fill_ump_ep_name(ump, dev, dev->descriptor.iProduct); /* fill fallback name */ if (!*ump->info.name) - sprintf(ump->info.name, "USB MIDI %d", rmidi->index); + scnprintf(ump->info.name, sizeof(ump->info.name), + "USB MIDI %d", rmidi->index); /* copy as rawmidi name if not set */ if (!*ump->core.name) strscpy(ump->core.name, ump->info.name, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index a7cd7d51e66b5..63b300bc67ba9 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -674,40 +674,40 @@ static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iter return 0; switch (iterm->type >> 16) { case UAC3_SELECTOR_UNIT: - strcpy(name, "Selector"); + strscpy(name, "Selector", maxlen); return 8; case UAC3_PROCESSING_UNIT: - strcpy(name, "Process Unit"); + strscpy(name, "Process Unit", maxlen); return 12; case UAC3_EXTENSION_UNIT: - strcpy(name, "Ext Unit"); + strscpy(name, "Ext Unit", maxlen); return 8; case UAC3_MIXER_UNIT: - strcpy(name, "Mixer"); + strscpy(name, "Mixer", maxlen); return 5; default: - return sprintf(name, "Unit %d", iterm->id); + return scnprintf(name, maxlen, "Unit %d", iterm->id); } } switch (iterm->type & 0xff00) { case 0x0100: - strcpy(name, "PCM"); + strscpy(name, "PCM", maxlen); return 3; case 0x0200: - strcpy(name, "Mic"); + strscpy(name, "Mic", maxlen); return 3; case 0x0400: - strcpy(name, "Headset"); + strscpy(name, "Headset", maxlen); return 7; case 0x0500: - strcpy(name, "Phone"); + strscpy(name, "Phone", maxlen); return 5; } for (names = iterm_names; names->type; names++) { if (names->type == iterm->type) { - strcpy(name, names->name); + strscpy(name, names->name, maxlen); return strlen(names->name); } } @@ -2804,7 +2804,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, len = get_term_name(state->chip, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); if (! len) - sprintf(namelist[i], "Input %u", i); + scnprintf(namelist[i], MAX_ITEM_NAME_LEN, "Input %u", i); } kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index ff548041679bb..8babfa3f7c458 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -357,21 +357,21 @@ static int scarlett_ctl_put(struct snd_kcontrol *kctl, return changed; } -static void scarlett_generate_name(int i, char *dst, int offsets[]) +static void scarlett_generate_name(int i, char *dst, size_t size, int offsets[]) { if (i > offsets[SCARLETT_OFFSET_MIX]) - sprintf(dst, "Mix %c", - 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); + scnprintf(dst, size, "Mix %c", + 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); else if (i > offsets[SCARLETT_OFFSET_ADAT]) - sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); + scnprintf(dst, size, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); else if (i > offsets[SCARLETT_OFFSET_SPDIF]) - sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); + scnprintf(dst, size, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); else if (i > offsets[SCARLETT_OFFSET_ANALOG]) - sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); + scnprintf(dst, size, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); else if (i > offsets[SCARLETT_OFFSET_PCM]) - sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); + scnprintf(dst, size, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); else - sprintf(dst, "Off"); + scnprintf(dst, size, "Off"); } static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, @@ -391,6 +391,7 @@ static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, /* generate name dynamically based on item number and offset info */ scarlett_generate_name(uinfo->value.enumerated.item, uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), opt->offsets); return 0; @@ -876,7 +877,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_IMPEDANCE: - sprintf(mx, "Input %d Impedance Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Impedance Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x09, ctl->num, USB_MIXER_S16, 1, mx, @@ -885,7 +887,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_PAD: - sprintf(mx, "Input %d Pad Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Pad Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x0b, ctl->num, USB_MIXER_S16, 1, mx, @@ -894,7 +897,8 @@ static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, return err; break; case SCARLETT_SWITCH_GAIN: - sprintf(mx, "Input %d Gain Switch", ctl->num); + scnprintf(mx, sizeof(mx), + "Input %d Gain Switch", ctl->num); err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, scarlett_ctl_enum_resume, 0x01, 0x08, ctl->num, USB_MIXER_S16, 1, mx, @@ -960,8 +964,9 @@ int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) return err; for (o = 0; o < info->matrix_out; o++) { - sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, - o+'A'); + scnprintf(mx, sizeof(mx), + "Matrix %02d Mix %c Playback Volume", i+1, + o+'A'); err = add_new_ctl(mixer, &usb_scarlett_ctl, scarlett_ctl_resume, 0x3c, 0x00, (i << 3) + (o & 0x07), USB_MIXER_S16, diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 93589e86828a3..49eeb1444dce2 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -7396,13 +7396,15 @@ static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, if (port_type == SCARLETT2_PORT_TYPE_MIX && item >= private->num_mix_out) - sprintf(uinfo->value.enumerated.name, - port->dsp_src_descr, - item - private->num_mix_out + 1); + scnprintf(uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), + port->dsp_src_descr, + item - private->num_mix_out + 1); else - sprintf(uinfo->value.enumerated.name, - port->src_descr, - item + port->src_num_offset); + scnprintf(uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), + port->src_descr, + item + port->src_num_offset); return 0; } diff --git a/sound/usb/proc.c b/sound/usb/proc.c index e9bbaea7b2fa3..c8b967bd7065e 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -231,7 +231,7 @@ void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream) char name[32]; struct snd_card *card = stream->chip->card; - sprintf(name, "stream%d", stream->pcm_index); + scnprintf(name, sizeof(name), "stream%d", stream->pcm_index); snd_card_ro_proc_new(card, name, stream, proc_pcm_format_read); } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 749498fbf9cb2..ad6ced7806347 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -536,7 +536,8 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, pcm->private_free = snd_usb_audio_pcm_free; pcm->info_flags = 0; if (chip->pcm_devs > 0) - sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); + scnprintf(pcm->name, sizeof(pcm->name), "USB Audio #%d", + chip->pcm_devs); else strscpy(pcm->name, "USB Audio"); -- GitLab From bee60f019606827363d7e355b2e859e29e928c3a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2025 12:07:25 +0200 Subject: [PATCH 631/868] ALSA: core: Copy string more safely Replace the remaining strcpy() and sprintf() usages in the ALSA core code with the safer versions. The first strcpy() points actually to card->id, hence just use strscpy() with card->id instead. The append of suffix string is slightly rewritten so that we can use scnprintf() and strscpy(). Only for safety, no actual behavior change. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250710100727.22653-104-tiwai@suse.de --- sound/core/init.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 114fb87de990e..c372b3228785e 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -723,27 +723,25 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, * ("card" conflicts with proc directories) */ if (!*id || !strncmp(id, "card", 4)) { - strcpy(id, "Default"); + strscpy(card->id, "Default"); is_default = true; } len = strlen(id); for (loops = 0; loops < SNDRV_CARDS; loops++) { - char *spos; char sfxstr[5]; /* "_012" */ - int sfxlen; + int sfxlen, slen; if (card_id_ok(card, id)) return; /* OK */ /* Add _XYZ suffix */ - sprintf(sfxstr, "_%X", loops + 1); - sfxlen = strlen(sfxstr); + sfxlen = scnprintf(sfxstr, sizeof(sfxstr), "_%X", loops + 1); if (len + sfxlen >= sizeof(card->id)) - spos = id + sizeof(card->id) - sfxlen - 1; + slen = sizeof(card->id) - sfxlen - 1; else - spos = id + len; - strcpy(spos, sfxstr); + slen = len; + strscpy(id + slen, sfxstr, sizeof(card->id) - slen); } /* fallback to the default id */ if (!is_default) { @@ -801,7 +799,7 @@ static ssize_t id_store(struct device *dev, struct device_attribute *attr, guard(mutex)(&snd_card_mutex); if (!card_id_ok(NULL, buf1)) return -EEXIST; - strcpy(card->id, buf1); + strscpy(card->id, buf1); snd_info_card_id_change(card); return count; -- GitLab From ed677858d4fe8d165952c1794898d6fc0b65ddfe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:04 +0200 Subject: [PATCH 632/868] ALSA: hda: Move widget capability macros into hdaudio.h The get_wcaps() and co are used not only by HD-audio core but also other driver code, hence it'd be better to put into the common header instead of local.h. OTOH, there are macros of the same name like get_wcaps() that are still used in sound/pci/hda/* locally, and those conflict with each other. So we need to rename get_wcaps() (to be moved from hda-core) with the proper snd_hdac prefix for avoiding name conflicts, and define in the common hdaudio.h. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-2-tiwai@suse.de --- include/sound/hdaudio.h | 24 ++++++++++++++++++++++++ sound/hda/hdac_device.c | 8 ++++---- sound/hda/hdac_sysfs.c | 14 +++++++------- sound/hda/local.h | 21 --------------------- sound/soc/codecs/hdac_hdmi.c | 21 ++++++++++----------- 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 25668eee65cf3..d38234f8fe446 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -680,6 +680,30 @@ static inline void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev, } #endif /* CONFIG_SND_HDA_DSP_LOADER */ +/* + * Easy macros for widget capabilities + */ +#define snd_hdac_get_wcaps(codec, nid) \ + snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) + +/* get the widget type from widget capability bits */ +static inline int snd_hdac_get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +/* get the number of supported channels */ +static inline unsigned int snd_hdac_get_wcaps_channels(u32 wcaps) +{ + unsigned int chans; + + chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; + chans = (chans + 1) * 2; + + return chans; +} /* * generic array helpers diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index a02dce5f6a889..018f9e176b1b8 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -436,11 +436,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); /* return CONNLIST_LEN parameter of the given widget */ static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) { - unsigned int wcaps = get_wcaps(codec, nid); + unsigned int wcaps = snd_hdac_get_wcaps(codec, nid); unsigned int parm; if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB) return 0; parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN); @@ -854,7 +854,7 @@ static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) unsigned int val = 0; if (nid != codec->afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + (snd_hdac_get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); if (!val || val == -1) val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); @@ -894,7 +894,7 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, { unsigned int i, val, wcaps; - wcaps = get_wcaps(codec, nid); + wcaps = snd_hdac_get_wcaps(codec, nid); val = query_pcm_param(codec, nid); if (ratesp) { diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 60b0a70428d56..bffe52859dbaa 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -161,13 +161,13 @@ static const struct kobj_type widget_ktype = { static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - return sysfs_emit(buf, "0x%08x\n", get_wcaps(codec, nid)); + return sysfs_emit(buf, "0x%08x\n", snd_hdac_get_wcaps(codec, nid)); } static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); @@ -178,7 +178,7 @@ static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, { unsigned int val; - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN) return 0; if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) return 0; @@ -189,7 +189,7 @@ static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) { if (nid == codec->afg || nid == codec->mfg) return true; - switch (get_wcaps_type(get_wcaps(codec, nid))) { + switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) { case AC_WID_AUD_OUT: case AC_WID_AUD_IN: return true; @@ -219,7 +219,7 @@ static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); @@ -228,7 +228,7 @@ static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); @@ -237,7 +237,7 @@ static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, struct widget_attribute *attr, char *buf) { - if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) + if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER)) return 0; return sysfs_emit(buf, "0x%08x\n", snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); diff --git a/sound/hda/local.h b/sound/hda/local.h index 896ba142e8bc0..5f03b203c4163 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -6,27 +6,6 @@ #ifndef __HDAC_LOCAL_H #define __HDAC_LOCAL_H -#define get_wcaps(codec, nid) \ - snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) - -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -static inline unsigned int get_wcaps_channels(u32 wcaps) -{ - unsigned int chans; - - chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; - chans = (chans + 1) * 2; - - return chans; -} - extern const struct attribute_group *hdac_dev_attr_groups[]; int hda_widget_sysfs_init(struct hdac_device *codec); int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid, diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 1139a2754ca33..b33cd5178008b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -24,7 +24,6 @@ #include #include #include -#include "../../hda/local.h" #include "hdac_hdmi.h" #define NAME_SIZE 32 @@ -221,8 +220,8 @@ static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) unsigned int caps; unsigned int type, param; - caps = get_wcaps(hdev, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) return 0; @@ -492,10 +491,10 @@ static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, struct hdac_hdmi_pin *pin, struct hdac_hdmi_port *port) { - if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + if (!(snd_hdac_get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { dev_warn(&hdev->dev, "HDMI: pin %d wcaps %#x does not support connection list\n", - pin->nid, get_wcaps(hdev, pin->nid)); + pin->nid, snd_hdac_get_wcaps(hdev, pin->nid)); return -EINVAL; } @@ -660,8 +659,8 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); int err; - chans = get_wcaps(hdev, cvt->nid); - chans = get_wcaps_channels(chans); + chans = snd_hdac_get_wcaps(hdev, cvt->nid); + chans = snd_hdac_get_wcaps_channels(chans); cvt->params.channels_min = 2; @@ -743,7 +742,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev, int count; unsigned int state; - if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_POWER) { if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { for (count = 0; count < 10; count++) { snd_hdac_codec_read(hdev, nid, 0, @@ -761,7 +760,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev, static void hdac_hdmi_set_amp(struct hdac_device *hdev, hda_nid_t nid, int val) { - if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) snd_hdac_codec_write(hdev, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); } @@ -1648,8 +1647,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, unsigned int caps; unsigned int type; - caps = get_wcaps(hdev, nid); - type = get_wcaps_type(caps); + caps = snd_hdac_get_wcaps(hdev, nid); + type = snd_hdac_get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL)) continue; -- GitLab From b2660d1ebde1ba8f3edf963f3aac2bea884457c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:05 +0200 Subject: [PATCH 633/868] ALSA: hda: Move HD-audio core stuff into sound/hda/core This is a part of HD-audio code restructuring. Simply move the current code of sound/hda/* into the subdirectory sound/hda/core, so that more stuff can be moved into sound/hda cleanly later. Most of file names with hdac_ and hdac_ext_ prefix are renamed without the prefix, since they can be identified well in the directory name and superfluous. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-3-tiwai@suse.de --- sound/hda/Kconfig | 67 +------------------ sound/hda/Makefile | 22 +----- sound/hda/core/Kconfig | 67 +++++++++++++++++++ sound/hda/core/Makefile | 22 ++++++ sound/hda/{ => core}/array.c | 0 sound/hda/{hdac_bus.c => core/bus.c} | 0 .../{hdac_component.c => core/component.c} | 0 .../{hdac_controller.c => core/controller.c} | 0 sound/hda/{hdac_device.c => core/device.c} | 0 sound/hda/{ => core}/ext/Makefile | 2 +- .../{ext/hdac_ext_bus.c => core/ext/bus.c} | 0 .../ext/controller.c} | 0 .../hdac_ext_stream.c => core/ext/stream.c} | 0 sound/hda/{ => core}/hda_bus_type.c | 0 sound/hda/{ => core}/hdmi_chmap.c | 0 sound/hda/{hdac_i915.c => core/i915.c} | 0 sound/hda/{ => core}/intel-dsp-config.c | 0 sound/hda/{ => core}/intel-nhlt.c | 0 sound/hda/{ => core}/intel-sdw-acpi.c | 0 sound/hda/{ => core}/local.h | 0 sound/hda/{hdac_regmap.c => core/regmap.c} | 0 sound/hda/{hdac_stream.c => core/stream.c} | 0 sound/hda/{hdac_sysfs.c => core/sysfs.c} | 0 sound/hda/{ => core}/trace.c | 0 sound/hda/{ => core}/trace.h | 0 25 files changed, 92 insertions(+), 88 deletions(-) create mode 100644 sound/hda/core/Kconfig create mode 100644 sound/hda/core/Makefile rename sound/hda/{ => core}/array.c (100%) rename sound/hda/{hdac_bus.c => core/bus.c} (100%) rename sound/hda/{hdac_component.c => core/component.c} (100%) rename sound/hda/{hdac_controller.c => core/controller.c} (100%) rename sound/hda/{hdac_device.c => core/device.c} (100%) rename sound/hda/{ => core}/ext/Makefile (54%) rename sound/hda/{ext/hdac_ext_bus.c => core/ext/bus.c} (100%) rename sound/hda/{ext/hdac_ext_controller.c => core/ext/controller.c} (100%) rename sound/hda/{ext/hdac_ext_stream.c => core/ext/stream.c} (100%) rename sound/hda/{ => core}/hda_bus_type.c (100%) rename sound/hda/{ => core}/hdmi_chmap.c (100%) rename sound/hda/{hdac_i915.c => core/i915.c} (100%) rename sound/hda/{ => core}/intel-dsp-config.c (100%) rename sound/hda/{ => core}/intel-nhlt.c (100%) rename sound/hda/{ => core}/intel-sdw-acpi.c (100%) rename sound/hda/{ => core}/local.h (100%) rename sound/hda/{hdac_regmap.c => core/regmap.c} (100%) rename sound/hda/{hdac_stream.c => core/stream.c} (100%) rename sound/hda/{hdac_sysfs.c => core/sysfs.c} (100%) rename sound/hda/{ => core}/trace.c (100%) rename sound/hda/{ => core}/trace.h (100%) diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index eb488a5225724..e380146560772 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,67 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_HDA_CORE - tristate - select REGMAP - -config SND_HDA_DSP_LOADER - bool - -config SND_HDA_ALIGNED_MMIO - bool - -config SND_HDA_COMPONENT - bool - -config SND_HDA_I915 - bool - select SND_HDA_COMPONENT - -config SND_HDA_EXT_CORE - tristate - select SND_HDA_CORE - -config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" - range 0 32768 - default 0 if SND_DMA_SGBUF - default 64 if !SND_DMA_SGBUF - help - Specifies the default pre-allocated buffer-size in kB for the - HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems using PulseAudio. The default 64 is chosen just - for compatibility reasons. - On x86 systems, the default is zero as S/G allocation works - and no preallocation is needed in most cases. - - Note that the pre-allocation size can be changed dynamically - via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. - -config SND_INTEL_NHLT - bool - # this config should be selected only for Intel ACPI platforms. - # A fallback is provided so that the code compiles in all cases. - -config SND_INTEL_DSP_CONFIG - tristate - select ACPI_NHLT if ACPI - select SND_INTEL_NHLT if ACPI - select SND_INTEL_SOUNDWIRE_ACPI if ACPI - # this config should be selected only for Intel DSP platforms. - # A fallback is provided so that the code compiles in all cases. - -config SND_INTEL_SOUNDWIRE_ACPI - tristate - -config SND_INTEL_BYT_PREFER_SOF - bool "Prefer SOF driver over SST on BY/CHT platforms" - depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL - default n - help - The kernel has 2 drivers for the Low Power Engine audio-block on - Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF - driver. If both drivers are enabled then the kernel will default - to using the old SST driver, unless told otherwise through the - snd_intel_dspcfg.dsp_driver module-parameter. - - Set this option to Y to make the kernel default to the new SOF - driver instead. +source "sound/hda/core/Kconfig" diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 83cceafe0d4c3..3fdbc22b15304 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,22 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -snd-hda-core-y := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o - -snd-hda-core-y += trace.o -CFLAGS_trace.o := -I$(src) - -# for sync with i915 gfx driver -snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += hdac_component.o -snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o - -obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o - -#extended hda -obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ - -snd-intel-dspcfg-y := intel-dsp-config.o -snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o -obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o - -snd-intel-sdw-acpi-y := intel-sdw-acpi.o -obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o +obj-y += core/ diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig new file mode 100644 index 0000000000000..eb488a5225724 --- /dev/null +++ b/sound/hda/core/Kconfig @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_HDA_CORE + tristate + select REGMAP + +config SND_HDA_DSP_LOADER + bool + +config SND_HDA_ALIGNED_MMIO + bool + +config SND_HDA_COMPONENT + bool + +config SND_HDA_I915 + bool + select SND_HDA_COMPONENT + +config SND_HDA_EXT_CORE + tristate + select SND_HDA_CORE + +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 0 if SND_DMA_SGBUF + default 64 if !SND_DMA_SGBUF + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. + On x86 systems, the default is zero as S/G allocation works + and no preallocation is needed in most cases. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. + +config SND_INTEL_NHLT + bool + # this config should be selected only for Intel ACPI platforms. + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_DSP_CONFIG + tristate + select ACPI_NHLT if ACPI + select SND_INTEL_NHLT if ACPI + select SND_INTEL_SOUNDWIRE_ACPI if ACPI + # this config should be selected only for Intel DSP platforms. + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_SOUNDWIRE_ACPI + tristate + +config SND_INTEL_BYT_PREFER_SOF + bool "Prefer SOF driver over SST on BY/CHT platforms" + depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL + default n + help + The kernel has 2 drivers for the Low Power Engine audio-block on + Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF + driver. If both drivers are enabled then the kernel will default + to using the old SST driver, unless told otherwise through the + snd_intel_dspcfg.dsp_driver module-parameter. + + Set this option to Y to make the kernel default to the new SOF + driver instead. diff --git a/sound/hda/core/Makefile b/sound/hda/core/Makefile new file mode 100644 index 0000000000000..89cb461430509 --- /dev/null +++ b/sound/hda/core/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-core-y := hda_bus_type.o bus.o device.o sysfs.o \ + regmap.o controller.o stream.o array.o hdmi_chmap.o + +snd-hda-core-y += trace.o +CFLAGS_trace.o := -I$(src) + +# for sync with i915 gfx driver +snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += component.o +snd-hda-core-$(CONFIG_SND_HDA_I915) += i915.o + +obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o + +#extended hda +obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ + +snd-intel-dspcfg-y := intel-dsp-config.o +snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o +obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o + +snd-intel-sdw-acpi-y := intel-sdw-acpi.o +obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o diff --git a/sound/hda/array.c b/sound/hda/core/array.c similarity index 100% rename from sound/hda/array.c rename to sound/hda/core/array.c diff --git a/sound/hda/hdac_bus.c b/sound/hda/core/bus.c similarity index 100% rename from sound/hda/hdac_bus.c rename to sound/hda/core/bus.c diff --git a/sound/hda/hdac_component.c b/sound/hda/core/component.c similarity index 100% rename from sound/hda/hdac_component.c rename to sound/hda/core/component.c diff --git a/sound/hda/hdac_controller.c b/sound/hda/core/controller.c similarity index 100% rename from sound/hda/hdac_controller.c rename to sound/hda/core/controller.c diff --git a/sound/hda/hdac_device.c b/sound/hda/core/device.c similarity index 100% rename from sound/hda/hdac_device.c rename to sound/hda/core/device.c diff --git a/sound/hda/ext/Makefile b/sound/hda/core/ext/Makefile similarity index 54% rename from sound/hda/ext/Makefile rename to sound/hda/core/ext/Makefile index 05883fb28d289..85190a7eb5de7 100644 --- a/sound/hda/ext/Makefile +++ b/sound/hda/core/ext/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-hda-ext-core-y := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o +snd-hda-ext-core-y := bus.o controller.o stream.o obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/core/ext/bus.c similarity index 100% rename from sound/hda/ext/hdac_ext_bus.c rename to sound/hda/core/ext/bus.c diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/core/ext/controller.c similarity index 100% rename from sound/hda/ext/hdac_ext_controller.c rename to sound/hda/core/ext/controller.c diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/core/ext/stream.c similarity index 100% rename from sound/hda/ext/hdac_ext_stream.c rename to sound/hda/core/ext/stream.c diff --git a/sound/hda/hda_bus_type.c b/sound/hda/core/hda_bus_type.c similarity index 100% rename from sound/hda/hda_bus_type.c rename to sound/hda/core/hda_bus_type.c diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c similarity index 100% rename from sound/hda/hdmi_chmap.c rename to sound/hda/core/hdmi_chmap.c diff --git a/sound/hda/hdac_i915.c b/sound/hda/core/i915.c similarity index 100% rename from sound/hda/hdac_i915.c rename to sound/hda/core/i915.c diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c similarity index 100% rename from sound/hda/intel-dsp-config.c rename to sound/hda/core/intel-dsp-config.c diff --git a/sound/hda/intel-nhlt.c b/sound/hda/core/intel-nhlt.c similarity index 100% rename from sound/hda/intel-nhlt.c rename to sound/hda/core/intel-nhlt.c diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/core/intel-sdw-acpi.c similarity index 100% rename from sound/hda/intel-sdw-acpi.c rename to sound/hda/core/intel-sdw-acpi.c diff --git a/sound/hda/local.h b/sound/hda/core/local.h similarity index 100% rename from sound/hda/local.h rename to sound/hda/core/local.h diff --git a/sound/hda/hdac_regmap.c b/sound/hda/core/regmap.c similarity index 100% rename from sound/hda/hdac_regmap.c rename to sound/hda/core/regmap.c diff --git a/sound/hda/hdac_stream.c b/sound/hda/core/stream.c similarity index 100% rename from sound/hda/hdac_stream.c rename to sound/hda/core/stream.c diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/core/sysfs.c similarity index 100% rename from sound/hda/hdac_sysfs.c rename to sound/hda/core/sysfs.c diff --git a/sound/hda/trace.c b/sound/hda/core/trace.c similarity index 100% rename from sound/hda/trace.c rename to sound/hda/core/trace.c diff --git a/sound/hda/trace.h b/sound/hda/core/trace.h similarity index 100% rename from sound/hda/trace.h rename to sound/hda/core/trace.h -- GitLab From 05be28fe8521f183f945d052d5019197e5934f0e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:06 +0200 Subject: [PATCH 634/868] ALSA: hda: Move common codec driver into sound/hda/common directory The snd-hda-codec module contains the most of common code used by both HD-audio controller and codec drivers, and it's basically independent from PCI. Let's move the code to sound/hda/common directory as a part of code reorganization. The hda_ prefix is dropped from the most of file names as it's rather superfluous. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-4-tiwai@suse.de --- sound/hda/Kconfig | 5 ++ sound/hda/Makefile | 1 + sound/hda/common/Kconfig | 80 +++++++++++++++++++ sound/hda/common/Makefile | 13 +++ .../common/auto_parser.c} | 0 .../{pci/hda/hda_beep.c => hda/common/beep.c} | 0 .../{pci/hda/hda_bind.c => hda/common/bind.c} | 0 .../hda/hda_codec.c => hda/common/codec.c} | 0 .../common/controller.c} | 2 +- .../common/controller_trace.h} | 2 +- .../{pci/hda => hda/common}/hda_auto_parser.h | 0 sound/{pci/hda => hda/common}/hda_beep.h | 0 .../{pci/hda => hda/common}/hda_controller.h | 0 sound/{pci/hda => hda/common}/hda_jack.h | 0 sound/{pci/hda => hda/common}/hda_local.h | 0 .../hda/hda_hwdep.c => hda/common/hwdep.c} | 0 .../{pci/hda/hda_jack.c => hda/common/jack.c} | 0 .../{pci/hda/hda_proc.c => hda/common/proc.c} | 0 .../hda/hda_sysfs.c => hda/common/sysfs.c} | 0 sound/pci/hda/Kconfig | 76 ------------------ sound/pci/hda/Makefile | 13 +-- 21 files changed, 103 insertions(+), 89 deletions(-) create mode 100644 sound/hda/common/Kconfig create mode 100644 sound/hda/common/Makefile rename sound/{pci/hda/hda_auto_parser.c => hda/common/auto_parser.c} (100%) rename sound/{pci/hda/hda_beep.c => hda/common/beep.c} (100%) rename sound/{pci/hda/hda_bind.c => hda/common/bind.c} (100%) rename sound/{pci/hda/hda_codec.c => hda/common/codec.c} (100%) rename sound/{pci/hda/hda_controller.c => hda/common/controller.c} (99%) rename sound/{pci/hda/hda_controller_trace.h => hda/common/controller_trace.h} (97%) rename sound/{pci/hda => hda/common}/hda_auto_parser.h (100%) rename sound/{pci/hda => hda/common}/hda_beep.h (100%) rename sound/{pci/hda => hda/common}/hda_controller.h (100%) rename sound/{pci/hda => hda/common}/hda_jack.h (100%) rename sound/{pci/hda => hda/common}/hda_local.h (100%) rename sound/{pci/hda/hda_hwdep.c => hda/common/hwdep.c} (100%) rename sound/{pci/hda/hda_jack.c => hda/common/jack.c} (100%) rename sound/{pci/hda/hda_proc.c => hda/common/proc.c} (100%) rename sound/{pci/hda/hda_sysfs.c => hda/common/sysfs.c} (100%) diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index e380146560772..2928cb570ec63 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,2 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only +menu "HD-Audio" + +source "sound/hda/common/Kconfig" source "sound/hda/core/Kconfig" + +endmenu diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 3fdbc22b15304..e7596bf736a46 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core/ +obj-$(CONFIG_SND_HDA) += common/ diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig new file mode 100644 index 0000000000000..d1e2bfd24c117 --- /dev/null +++ b/sound/hda/common/Kconfig @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_HDA + tristate + select SND_PCM + select SND_VMASTER + select SND_JACK + select SND_HDA_CORE + +if SND_HDA + +config SND_HDA_HWDEP + bool "Build hwdep interface for HD-audio driver" + select SND_HWDEP + help + Say Y here to build a hwdep interface for HD-audio driver. + This interface can be used for out-of-band communication + with codecs for debugging purposes. + +config SND_HDA_RECONFIG + bool "Allow dynamic codec reconfiguration" + help + Say Y here to enable the HD-audio codec re-configuration feature. + It allows user to clear the whole codec configuration, change the + codec setup, add extra verbs, and re-configure the codec dynamically. + + Note that this item alone doesn't provide the sysfs interface, but + enables the feature just for the patch loader below. + If you need the traditional sysfs entries for the manual interaction, + turn on CONFIG_SND_HDA_HWDEP as well. + +config SND_HDA_INPUT_BEEP + bool "Support digital beep via input layer" + depends on INPUT=y || INPUT=SND_HDA + help + Say Y here to build a digital beep interface for HD-audio + driver. This interface is used to generate digital beeps. + +config SND_HDA_INPUT_BEEP_MODE + int "Digital beep registration mode (0=off, 1=on)" + depends on SND_HDA_INPUT_BEEP=y + default "1" + range 0 1 + help + Set 0 to disable the digital beep interface for HD-audio by default. + Set 1 to always enable the digital beep interface for HD-audio by + default. + +config SND_HDA_PATCH_LOADER + bool "Support initialization patch loading for HD-audio" + select FW_LOADER + select SND_HDA_RECONFIG + help + Say Y here to allow the HD-audio driver to load a pseudo + firmware file ("patch") for overriding the BIOS setup at + start up. The "patch" file can be specified via patch module + option, such as patch=hda-init. + +config SND_HDA_POWER_SAVE_DEFAULT + int "Default time-out for HD-audio power-save mode" + depends on PM + default 0 + help + The default time-out value in seconds for HD-audio automatic + power-save mode. 0 means to disable the power-save mode. + +config SND_HDA_CTL_DEV_ID + bool "Use the device identifier field for controls" + depends on SND_HDA_INTEL + help + Say Y to use the device identifier field for (mixer) + controls (old behaviour until this option is available). + + When enabled, the multiple HDA codecs may set the device + field in control (mixer) element identifiers. The use + of this field is not recommended and defined for mixer controls. + + The old behaviour (Y) is obsolete and will be removed. Consider + to not enable this option. +endif diff --git a/sound/hda/common/Makefile b/sound/hda/common/Makefile new file mode 100644 index 0000000000000..3344fa0efe758 --- /dev/null +++ b/sound/hda/common/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-codec-y := bind.o codec.o jack.o auto_parser.o sysfs.o +snd-hda-codec-y += controller.o +snd-hda-codec-$(CONFIG_SND_PROC_FS) += proc.o + +snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hwdep.o +snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += beep.o + +# for trace-points +CFLAGS_controller.o := -I$(src) + +# common driver +obj-$(CONFIG_SND_HDA) := snd-hda-codec.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/hda/common/auto_parser.c similarity index 100% rename from sound/pci/hda/hda_auto_parser.c rename to sound/hda/common/auto_parser.c diff --git a/sound/pci/hda/hda_beep.c b/sound/hda/common/beep.c similarity index 100% rename from sound/pci/hda/hda_beep.c rename to sound/hda/common/beep.c diff --git a/sound/pci/hda/hda_bind.c b/sound/hda/common/bind.c similarity index 100% rename from sound/pci/hda/hda_bind.c rename to sound/hda/common/bind.c diff --git a/sound/pci/hda/hda_codec.c b/sound/hda/common/codec.c similarity index 100% rename from sound/pci/hda/hda_codec.c rename to sound/hda/common/codec.c diff --git a/sound/pci/hda/hda_controller.c b/sound/hda/common/controller.c similarity index 99% rename from sound/pci/hda/hda_controller.c rename to sound/hda/common/controller.c index f3330b7e0fcfc..84387ed761be9 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/hda/common/controller.c @@ -29,7 +29,7 @@ #include "hda_local.h" #define CREATE_TRACE_POINTS -#include "hda_controller_trace.h" +#include "controller_trace.h" /* DSP lock helpers */ #define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/hda/common/controller_trace.h similarity index 97% rename from sound/pci/hda/hda_controller_trace.h rename to sound/hda/common/controller_trace.h index bf48304e230aa..7f5841f8919ec 100644 --- a/sound/pci/hda/hda_controller_trace.h +++ b/sound/hda/common/controller_trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM #define TRACE_SYSTEM hda_controller -#define TRACE_INCLUDE_FILE hda_controller_trace +#define TRACE_INCLUDE_FILE controller_trace #if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_HDA_CONTROLLER_H diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h similarity index 100% rename from sound/pci/hda/hda_auto_parser.h rename to sound/hda/common/hda_auto_parser.h diff --git a/sound/pci/hda/hda_beep.h b/sound/hda/common/hda_beep.h similarity index 100% rename from sound/pci/hda/hda_beep.h rename to sound/hda/common/hda_beep.h diff --git a/sound/pci/hda/hda_controller.h b/sound/hda/common/hda_controller.h similarity index 100% rename from sound/pci/hda/hda_controller.h rename to sound/hda/common/hda_controller.h diff --git a/sound/pci/hda/hda_jack.h b/sound/hda/common/hda_jack.h similarity index 100% rename from sound/pci/hda/hda_jack.h rename to sound/hda/common/hda_jack.h diff --git a/sound/pci/hda/hda_local.h b/sound/hda/common/hda_local.h similarity index 100% rename from sound/pci/hda/hda_local.h rename to sound/hda/common/hda_local.h diff --git a/sound/pci/hda/hda_hwdep.c b/sound/hda/common/hwdep.c similarity index 100% rename from sound/pci/hda/hda_hwdep.c rename to sound/hda/common/hwdep.c diff --git a/sound/pci/hda/hda_jack.c b/sound/hda/common/jack.c similarity index 100% rename from sound/pci/hda/hda_jack.c rename to sound/hda/common/jack.c diff --git a/sound/pci/hda/hda_proc.c b/sound/hda/common/proc.c similarity index 100% rename from sound/pci/hda/hda_proc.c rename to sound/hda/common/proc.c diff --git a/sound/pci/hda/hda_sysfs.c b/sound/hda/common/sysfs.c similarity index 100% rename from sound/pci/hda/hda_sysfs.c rename to sound/hda/common/sysfs.c diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 745f120a5cee2..1dfd56493d399 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -1,13 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "HD-Audio" -config SND_HDA - tristate - select SND_PCM - select SND_VMASTER - select SND_JACK - select SND_HDA_CORE - config SND_HDA_GENERIC_LEDS bool @@ -55,53 +48,6 @@ config SND_HDA_ACPI if SND_HDA -config SND_HDA_HWDEP - bool "Build hwdep interface for HD-audio driver" - select SND_HWDEP - help - Say Y here to build a hwdep interface for HD-audio driver. - This interface can be used for out-of-band communication - with codecs for debugging purposes. - -config SND_HDA_RECONFIG - bool "Allow dynamic codec reconfiguration" - help - Say Y here to enable the HD-audio codec re-configuration feature. - It allows user to clear the whole codec configuration, change the - codec setup, add extra verbs, and re-configure the codec dynamically. - - Note that this item alone doesn't provide the sysfs interface, but - enables the feature just for the patch loader below. - If you need the traditional sysfs entries for the manual interaction, - turn on CONFIG_SND_HDA_HWDEP as well. - -config SND_HDA_INPUT_BEEP - bool "Support digital beep via input layer" - depends on INPUT=y || INPUT=SND_HDA - help - Say Y here to build a digital beep interface for HD-audio - driver. This interface is used to generate digital beeps. - -config SND_HDA_INPUT_BEEP_MODE - int "Digital beep registration mode (0=off, 1=on)" - depends on SND_HDA_INPUT_BEEP=y - default "1" - range 0 1 - help - Set 0 to disable the digital beep interface for HD-audio by default. - Set 1 to always enable the digital beep interface for HD-audio by - default. - -config SND_HDA_PATCH_LOADER - bool "Support initialization patch loading for HD-audio" - select FW_LOADER - select SND_HDA_RECONFIG - help - Say Y here to allow the HD-audio driver to load a pseudo - firmware file ("patch") for overriding the BIOS setup at - start up. The "patch" file can be specified via patch module - option, such as patch=hda-init. - config SND_HDA_CIRRUS_SCODEC tristate @@ -394,14 +340,6 @@ config SND_HDA_GENERIC comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_GENERIC=m -config SND_HDA_POWER_SAVE_DEFAULT - int "Default time-out for HD-audio power-save mode" - depends on PM - default 0 - help - The default time-out value in seconds for HD-audio automatic - power-save mode. 0 means to disable the power-save mode. - config SND_HDA_INTEL_HDMI_SILENT_STREAM bool "Enable Silent Stream always for HDMI" depends on SND_HDA_INTEL @@ -417,20 +355,6 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM This feature can impact power consumption as resources are kept reserved both at transmitter and receiver. -config SND_HDA_CTL_DEV_ID - bool "Use the device identifier field for controls" - depends on SND_HDA_INTEL - help - Say Y to use the device identifier field for (mixer) - controls (old behaviour until this option is available). - - When enabled, the multiple HDA codecs may set the device - field in control (mixer) element identifiers. The use - of this field is not recommended and defined for mixer controls. - - The old behaviour (Y) is obsolete and will be removed. Consider - to not enable this option. - endif endmenu diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index a5ab8ee2d7f94..920011bb1b06c 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -3,17 +3,11 @@ snd-hda-intel-y := hda_intel.o snd-hda-tegra-y := hda_tegra.o snd-hda-acpi-y := hda_acpi.o -snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o -snd-hda-codec-y += hda_controller.o -snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o - -snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o -snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o - # for trace-points -CFLAGS_hda_controller.o := -I$(src) CFLAGS_hda_intel.o := -I$(src) +subdir-ccflags-y += -I$(src)/../../hda/common + snd-hda-codec-generic-y := hda_generic.o snd-hda-codec-realtek-y := patch_realtek.o snd-hda-codec-cmedia-y := patch_cmedia.o @@ -43,9 +37,6 @@ snd-hda-scodec-tas2781-y := tas2781_hda.o snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o -# common driver -obj-$(CONFIG_SND_HDA) := snd-hda-codec.o - # codec drivers obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o -- GitLab From 146355ee880e6d15ba12e2baf5be70e5efe07796 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:07 +0200 Subject: [PATCH 635/868] ALSA: hda: Move CONFIG_SND_HDA_PREALLOC_SIZE into sound/hda/common CONFIG_SND_HDA_PREALLOC_SIZE is used only by controller.c in sound/hda/common, hence it depends on CONFIG_SND_HDA. Move the definition to the right place inside SND_HDA if/endif block in sound/hda/common/Kconfig. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-5-tiwai@suse.de --- sound/hda/common/Kconfig | 17 +++++++++++++++++ sound/hda/core/Kconfig | 16 ---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig index d1e2bfd24c117..f38e1947fb3eb 100644 --- a/sound/hda/common/Kconfig +++ b/sound/hda/common/Kconfig @@ -77,4 +77,21 @@ config SND_HDA_CTL_DEV_ID The old behaviour (Y) is obsolete and will be removed. Consider to not enable this option. + +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 0 if SND_DMA_SGBUF + default 64 if !SND_DMA_SGBUF + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. + On x86 systems, the default is zero as S/G allocation works + and no preallocation is needed in most cases. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. + endif diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig index eb488a5225724..bfdcf6384c521 100644 --- a/sound/hda/core/Kconfig +++ b/sound/hda/core/Kconfig @@ -20,22 +20,6 @@ config SND_HDA_EXT_CORE tristate select SND_HDA_CORE -config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" - range 0 32768 - default 0 if SND_DMA_SGBUF - default 64 if !SND_DMA_SGBUF - help - Specifies the default pre-allocated buffer-size in kB for the - HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems using PulseAudio. The default 64 is chosen just - for compatibility reasons. - On x86 systems, the default is zero as S/G allocation works - and no preallocation is needed in most cases. - - Note that the pre-allocation size can be changed dynamically - via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. - config SND_INTEL_NHLT bool # this config should be selected only for Intel ACPI platforms. -- GitLab From 2d9223d2d64cb5216859ced6cc33fb3b04e5f98f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:08 +0200 Subject: [PATCH 636/868] ALSA: hda: Move controller drivers into sound/hda/controllers directory Now HD-audio controller drivers are moved into sound/hda/controllers directory as a part of HD-audio code reorganization. Most of drivers are independent from PCI bus, hence it makes more sense to put under sound/hda. The hda_ prefix is dropped from most of files at moving, as it's more or less superfluous. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-6-tiwai@suse.de --- sound/hda/Kconfig | 1 + sound/hda/Makefile | 4 ++ sound/hda/controllers/Kconfig | 42 +++++++++++++++++++ sound/hda/controllers/Makefile | 13 ++++++ .../hda/hda_acpi.c => hda/controllers/acpi.c} | 0 .../hda_intel.c => hda/controllers/intel.c} | 5 +-- .../hda_intel.h => hda/controllers/intel.h} | 0 .../controllers/intel_trace.h} | 2 +- .../hda_tegra.c => hda/controllers/tegra.c} | 0 sound/pci/hda/Kconfig | 42 ------------------- sound/pci/hda/Makefile | 14 ------- 11 files changed, 63 insertions(+), 60 deletions(-) create mode 100644 sound/hda/controllers/Kconfig create mode 100644 sound/hda/controllers/Makefile rename sound/{pci/hda/hda_acpi.c => hda/controllers/acpi.c} (100%) rename sound/{pci/hda/hda_intel.c => hda/controllers/intel.c} (99%) rename sound/{pci/hda/hda_intel.h => hda/controllers/intel.h} (100%) rename sound/{pci/hda/hda_intel_trace.h => hda/controllers/intel_trace.h} (95%) rename sound/{pci/hda/hda_tegra.c => hda/controllers/tegra.c} (100%) diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 2928cb570ec63..d360c884bd6d0 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -2,6 +2,7 @@ menu "HD-Audio" source "sound/hda/common/Kconfig" +source "sound/hda/controllers/Kconfig" source "sound/hda/core/Kconfig" endmenu diff --git a/sound/hda/Makefile b/sound/hda/Makefile index e7596bf736a46..fc76086a1f5ed 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,3 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core/ obj-$(CONFIG_SND_HDA) += common/ +# this must be the last entry after codec drivers; +# otherwise the codec patches won't be hooked before the PCI probe +# when built in kernel +obj-$(CONFIG_SND_HDA) += controllers/ diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig new file mode 100644 index 0000000000000..34721f50b055a --- /dev/null +++ b/sound/hda/controllers/Kconfig @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SND_HDA_INTEL + tristate "HD Audio PCI" + depends on SND_PCI + select SND_HDA + select SND_INTEL_DSP_CONFIG + help + Say Y here to include support for Intel "High Definition + Audio" (Azalia) and its compatible devices. + + This option enables the HD-audio controller. Don't forget + to choose the appropriate HD-audio codec options. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-intel. + +config SND_HDA_TEGRA + tristate "NVIDIA Tegra HD Audio" + depends on ARCH_TEGRA + select SND_HDA + select SND_HDA_ALIGNED_MMIO + help + Say Y here to support the HDA controller present in NVIDIA + Tegra SoCs + + This options enables support for the HD Audio controller + present in some NVIDIA Tegra SoCs, used to communicate audio + to the HDMI output. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-tegra. + +config SND_HDA_ACPI + tristate "HD Audio ACPI" + depends on ACPI + select SND_HDA + help + Say Y here to include support for Azalia-compatible HDA controllers + which are advertised via ACPI objects. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-acpi. diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile new file mode 100644 index 0000000000000..a4bcd055e9aef --- /dev/null +++ b/sound/hda/controllers/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-hda-intel-y := intel.o +snd-hda-tegra-y := tegra.o +snd-hda-acpi-y := acpi.o + +subdir-ccflags-y += -I$(src)/../common + +# for trace-points +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_ACPI) += snd-hda-acpi.o diff --git a/sound/pci/hda/hda_acpi.c b/sound/hda/controllers/acpi.c similarity index 100% rename from sound/pci/hda/hda_acpi.c rename to sound/hda/controllers/acpi.c diff --git a/sound/pci/hda/hda_intel.c b/sound/hda/controllers/intel.c similarity index 99% rename from sound/pci/hda/hda_intel.c rename to sound/hda/controllers/intel.c index 439cf1bda6e66..ebfc999156f4e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/hda/controllers/intel.c @@ -54,11 +54,10 @@ #include #include #include -#include "hda_controller.h" -#include "hda_intel.h" +#include "intel.h" #define CREATE_TRACE_POINTS -#include "hda_intel_trace.h" +#include "intel_trace.h" /* position fix mode */ enum { diff --git a/sound/pci/hda/hda_intel.h b/sound/hda/controllers/intel.h similarity index 100% rename from sound/pci/hda/hda_intel.h rename to sound/hda/controllers/intel.h diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/hda/controllers/intel_trace.h similarity index 95% rename from sound/pci/hda/hda_intel_trace.h rename to sound/hda/controllers/intel_trace.h index 2775fa81a5007..fb10ab9e7e552 100644 --- a/sound/pci/hda/hda_intel_trace.h +++ b/sound/hda/controllers/intel_trace.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM #define TRACE_SYSTEM hda_intel -#define TRACE_INCLUDE_FILE hda_intel_trace +#define TRACE_INCLUDE_FILE intel_trace #if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_HDA_INTEL_H diff --git a/sound/pci/hda/hda_tegra.c b/sound/hda/controllers/tegra.c similarity index 100% rename from sound/pci/hda/hda_tegra.c rename to sound/hda/controllers/tegra.c diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 1dfd56493d399..a5d345514cf3f 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -4,48 +4,6 @@ menu "HD-Audio" config SND_HDA_GENERIC_LEDS bool -config SND_HDA_INTEL - tristate "HD Audio PCI" - depends on SND_PCI - select SND_HDA - select SND_INTEL_DSP_CONFIG - help - Say Y here to include support for Intel "High Definition - Audio" (Azalia) and its compatible devices. - - This option enables the HD-audio controller. Don't forget - to choose the appropriate codec options below. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-intel. - -config SND_HDA_TEGRA - tristate "NVIDIA Tegra HD Audio" - depends on ARCH_TEGRA - select SND_HDA - select SND_HDA_ALIGNED_MMIO - help - Say Y here to support the HDA controller present in NVIDIA - Tegra SoCs - - This options enables support for the HD Audio controller - present in some NVIDIA Tegra SoCs, used to communicate audio - to the HDMI output. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-tegra. - -config SND_HDA_ACPI - tristate "HD Audio ACPI" - depends on ACPI - select SND_HDA - help - Say Y here to include support for Azalia-compatible HDA controllers - which are advertised via ACPI objects. - - To compile this driver as a module, choose M here: the module - will be called snd-hda-acpi. - if SND_HDA config SND_HDA_CIRRUS_SCODEC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 920011bb1b06c..79de0af71ad46 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,11 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -snd-hda-intel-y := hda_intel.o -snd-hda-tegra-y := hda_tegra.o -snd-hda-acpi-y := hda_acpi.o - -# for trace-points -CFLAGS_hda_intel.o := -I$(src) - subdir-ccflags-y += -I$(src)/../../hda/common snd-hda-codec-generic-y := hda_generic.o @@ -66,10 +59,3 @@ obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o - -# this must be the last entry after codec drivers; -# otherwise the codec patches won't be hooked before the PCI probe -# when built in kernel -obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o -obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o -obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o -- GitLab From 6014e9021b28e634935c776c0271b5cbcabdc5d6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:09 +0200 Subject: [PATCH 637/868] ALSA: hda: Move codec drivers into sound/hda/codecs directory Now move the all remaining codec drivers from sound/pci/hda to sound/hda/codecs subdirectory. Some drivers are put under the further vendor subdirectory, and the vendor helper code (*_helper.c) are put under helpers subdirectory. Also the sub-codec drivers are moved under a different subdirectory, sound/hda/codecs/sub-codecs, for distinguishing from the main HD-audio codec drivers. The prefix patch_ and hda_ as well as the suffix _helper are dropped from file names as they are mostly superfluous. No functional changes but just file path shuffling. Reviewed-by: Richard Fitzgerald Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-7-tiwai@suse.de --- sound/hda/Kconfig | 1 + sound/hda/Makefile | 1 + sound/{pci/hda => hda/codecs}/Kconfig | 158 +----------------- sound/hda/codecs/Makefile | 31 ++++ .../patch_analog.c => hda/codecs/analog.c} | 2 +- .../patch_ca0110.c => hda/codecs/ca0110.c} | 2 +- .../patch_ca0132.c => hda/codecs/ca0132.c} | 2 +- sound/{pci/hda => hda/codecs}/ca0132_regs.h | 0 sound/hda/codecs/cirrus/Kconfig | 21 +++ sound/hda/codecs/cirrus/Makefile | 8 + .../codecs/cirrus/cirrus.c} | 2 +- .../codecs/cirrus/cs8409-tables.c} | 4 +- .../codecs/cirrus/cs8409.c} | 2 +- .../codecs/cirrus/cs8409.h} | 2 +- .../patch_cmedia.c => hda/codecs/cmedia.c} | 2 +- .../codecs/conexant.c} | 6 +- .../hda_generic.c => hda/codecs/generic.c} | 2 +- .../hda_generic.h => hda/codecs/generic.h} | 0 sound/hda/codecs/hdmi/Makefile | 6 + .../hda/hda_eld.c => hda/codecs/hdmi/eld.c} | 0 .../patch_hdmi.c => hda/codecs/hdmi/hdmi.c} | 2 +- .../codecs/helpers/hp_x360.c} | 0 .../codecs/helpers/ideapad_hotkey_led.c} | 0 .../codecs/helpers/ideapad_s740.c} | 0 .../codecs/helpers/thinkpad.c} | 0 .../patch_realtek.c => hda/codecs/realtek.c} | 12 +- .../codecs/senarytech.c} | 4 +- .../patch_si3054.c => hda/codecs/si3054.c} | 0 sound/hda/codecs/side-codecs/Kconfig | 128 ++++++++++++++ sound/hda/codecs/side-codecs/Makefile | 28 ++++ .../codecs/side-codecs}/cirrus_scodec.c | 0 .../codecs/side-codecs}/cirrus_scodec.h | 0 .../codecs/side-codecs}/cirrus_scodec_test.c | 0 .../codecs/side-codecs}/cs35l41_hda.c | 2 +- .../codecs/side-codecs}/cs35l41_hda.h | 0 .../codecs/side-codecs}/cs35l41_hda_i2c.c | 0 .../side-codecs}/cs35l41_hda_property.c | 0 .../side-codecs}/cs35l41_hda_property.h | 0 .../codecs/side-codecs}/cs35l41_hda_spi.c | 0 .../codecs/side-codecs}/cs35l56_hda.c | 2 +- .../codecs/side-codecs}/cs35l56_hda.h | 0 .../codecs/side-codecs}/cs35l56_hda_i2c.c | 0 .../codecs/side-codecs}/cs35l56_hda_spi.c | 0 .../codecs/side-codecs}/hda_component.c | 0 .../codecs/side-codecs}/hda_component.h | 0 .../codecs/side-codecs}/tas2781_hda.c | 0 .../codecs/side-codecs}/tas2781_hda.h | 0 .../codecs/side-codecs}/tas2781_hda_i2c.c | 2 +- .../codecs/side-codecs}/tas2781_hda_spi.c | 2 +- .../codecs/sigmatel.c} | 4 +- .../{pci/hda/patch_via.c => hda/codecs/via.c} | 2 +- sound/pci/Kconfig | 2 - sound/pci/Makefile | 1 - sound/pci/hda/Makefile | 61 ------- 54 files changed, 257 insertions(+), 247 deletions(-) rename sound/{pci/hda => hda/codecs}/Kconfig (55%) create mode 100644 sound/hda/codecs/Makefile rename sound/{pci/hda/patch_analog.c => hda/codecs/analog.c} (99%) rename sound/{pci/hda/patch_ca0110.c => hda/codecs/ca0110.c} (98%) rename sound/{pci/hda/patch_ca0132.c => hda/codecs/ca0132.c} (99%) rename sound/{pci/hda => hda/codecs}/ca0132_regs.h (100%) create mode 100644 sound/hda/codecs/cirrus/Kconfig create mode 100644 sound/hda/codecs/cirrus/Makefile rename sound/{pci/hda/patch_cirrus.c => hda/codecs/cirrus/cirrus.c} (99%) rename sound/{pci/hda/patch_cs8409-tables.c => hda/codecs/cirrus/cs8409-tables.c} (99%) rename sound/{pci/hda/patch_cs8409.c => hda/codecs/cirrus/cs8409.c} (99%) rename sound/{pci/hda/patch_cs8409.h => hda/codecs/cirrus/cs8409.h} (99%) rename sound/{pci/hda/patch_cmedia.c => hda/codecs/cmedia.c} (99%) rename sound/{pci/hda/patch_conexant.c => hda/codecs/conexant.c} (99%) rename sound/{pci/hda/hda_generic.c => hda/codecs/generic.c} (99%) rename sound/{pci/hda/hda_generic.h => hda/codecs/generic.h} (100%) create mode 100644 sound/hda/codecs/hdmi/Makefile rename sound/{pci/hda/hda_eld.c => hda/codecs/hdmi/eld.c} (100%) rename sound/{pci/hda/patch_hdmi.c => hda/codecs/hdmi/hdmi.c} (99%) rename sound/{pci/hda/hp_x360_helper.c => hda/codecs/helpers/hp_x360.c} (100%) rename sound/{pci/hda/ideapad_hotkey_led_helper.c => hda/codecs/helpers/ideapad_hotkey_led.c} (100%) rename sound/{pci/hda/ideapad_s740_helper.c => hda/codecs/helpers/ideapad_s740.c} (100%) rename sound/{pci/hda/thinkpad_helper.c => hda/codecs/helpers/thinkpad.c} (100%) rename sound/{pci/hda/patch_realtek.c => hda/codecs/realtek.c} (99%) rename sound/{pci/hda/patch_senarytech.c => hda/codecs/senarytech.c} (98%) rename sound/{pci/hda/patch_si3054.c => hda/codecs/si3054.c} (100%) create mode 100644 sound/hda/codecs/side-codecs/Kconfig create mode 100644 sound/hda/codecs/side-codecs/Makefile rename sound/{pci/hda => hda/codecs/side-codecs}/cirrus_scodec.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cirrus_scodec.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cirrus_scodec_test.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda.c (99%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda_i2c.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda_property.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda_property.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l41_hda_spi.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l56_hda.c (99%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l56_hda.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l56_hda_i2c.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/cs35l56_hda_spi.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/hda_component.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/hda_component.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/tas2781_hda.c (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/tas2781_hda.h (100%) rename sound/{pci/hda => hda/codecs/side-codecs}/tas2781_hda_i2c.c (99%) rename sound/{pci/hda => hda/codecs/side-codecs}/tas2781_hda_spi.c (99%) rename sound/{pci/hda/patch_sigmatel.c => hda/codecs/sigmatel.c} (99%) rename sound/{pci/hda/patch_via.c => hda/codecs/via.c} (99%) delete mode 100644 sound/pci/hda/Makefile diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index d360c884bd6d0..7797f44b3d0c4 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -3,6 +3,7 @@ menu "HD-Audio" source "sound/hda/common/Kconfig" source "sound/hda/controllers/Kconfig" +source "sound/hda/codecs/Kconfig" source "sound/hda/core/Kconfig" endmenu diff --git a/sound/hda/Makefile b/sound/hda/Makefile index fc76086a1f5ed..31b9fbedaa77e 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core/ obj-$(CONFIG_SND_HDA) += common/ +obj-$(CONFIG_SND_HDA) += codecs/ # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe # when built in kernel diff --git a/sound/pci/hda/Kconfig b/sound/hda/codecs/Kconfig similarity index 55% rename from sound/pci/hda/Kconfig rename to sound/hda/codecs/Kconfig index a5d345514cf3f..bac19a664be0f 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/hda/codecs/Kconfig @@ -1,140 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -menu "HD-Audio" +if SND_HDA config SND_HDA_GENERIC_LEDS bool -if SND_HDA - -config SND_HDA_CIRRUS_SCODEC - tristate - -config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST - tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS - depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT - default KUNIT_ALL_TESTS - help - This builds KUnit tests for the cirrus side-codec library. - For more information on KUnit and unit tests in general, - please refer to the KUnit documentation in - Documentation/dev-tools/kunit/. - If in doubt, say "N". - -config SND_HDA_SCODEC_CS35L41 - tristate - select SND_HDA_GENERIC - select REGMAP_IRQ - select FW_CS_DSP - -config SND_HDA_SCODEC_COMPONENT - tristate - -config SND_HDA_SCODEC_CS35L41_I2C - tristate "Build CS35L41 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m - -config SND_HDA_SCODEC_CS35L41_SPI - tristate "Build CS35L41 HD-audio codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_SOC_CS35L41_LIB - select SND_HDA_SCODEC_CS35L41 - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L41 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m - -config SND_HDA_SCODEC_CS35L56 - tristate - -config SND_HDA_SCODEC_CS35L56_I2C - tristate "Build CS35L56 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - I2C control. - -config SND_HDA_SCODEC_CS35L56_SPI - tristate "Build CS35L56 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on SND_SOC - select FW_CS_DSP - imply SERIAL_MULTI_INSTANTIATE - select SND_HDA_GENERIC - select SND_SOC_CS35L56_SHARED - select SND_HDA_SCODEC_CS35L56 - select SND_HDA_CIRRUS_SCODEC - select SND_SOC_CS_AMP_LIB - help - Say Y or M here to include CS35L56 amplifier support with - SPI control. - -config SND_HDA_SCODEC_TAS2781 - tristate - select SND_HDA_GENERIC - -config SND_HDA_SCODEC_TAS2781_I2C - tristate "Build TAS2781 HD-audio side codec support for I2C Bus" - depends on I2C - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB_I2C - select SND_SOC_TAS2781_FMWLIB - select CRC32 - help - Say Y or M here to include TAS2781 I2C HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m - -config SND_HDA_SCODEC_TAS2781_SPI - tristate "Build TAS2781 HD-audio side codec support for SPI Bus" - depends on SPI_MASTER - depends on ACPI - depends on EFI - depends on SND_SOC - select SND_HDA_SCODEC_TAS2781 - select SND_SOC_TAS2781_COMLIB - select SND_SOC_TAS2781_FMWLIB - select CRC8 - select CRC32 - help - Say Y or M here to include TAS2781 SPI HD-audio side codec support - in snd-hda-intel driver, such as ALC287. - -comment "Set to Y if you want auto-loading the side codec driver" - depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m - config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" depends on INPUT @@ -194,26 +63,6 @@ config SND_HDA_CODEC_HDMI comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m -config SND_HDA_CODEC_CIRRUS - tristate "Build Cirrus Logic codec support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic codec support in - snd-hda-intel driver, such as CS4206. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m - -config SND_HDA_CODEC_CS8409 - tristate "Build Cirrus Logic HDA bridge support" - select SND_HDA_GENERIC - help - Say Y or M here to include Cirrus Logic HDA bridge support in - snd-hda-intel driver, such as CS8409. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m - config SND_HDA_CODEC_CONEXANT tristate "Build Conexant HD-audio codec support" select SND_HDA_GENERIC @@ -313,6 +162,7 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM This feature can impact power consumption as resources are kept reserved both at transmitter and receiver. -endif +source "sound/hda/codecs/cirrus/Kconfig" +source "sound/hda/codecs/side-codecs/Kconfig" -endmenu +endif # SND_HDA diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile new file mode 100644 index 0000000000000..205cd1373b421 --- /dev/null +++ b/sound/hda/codecs/Makefile @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../common + +snd-hda-codec-generic-y := generic.o +snd-hda-codec-analog-y := analog.o +snd-hda-codec-ca0110-y := ca0110.o +snd-hda-codec-ca0132-y := ca0132.o +snd-hda-codec-cmedia-y := cmedia.o +snd-hda-codec-conexant-y := conexant.o +snd-hda-codec-idt-y := sigmatel.o +snd-hda-codec-realtek-y := realtek.o +snd-hda-codec-senarytech-y := senarytech.o +snd-hda-codec-si3054-y := si3054.o +snd-hda-codec-via-y := via.o + +obj-y += cirrus/ +obj-y += hdmi/ +obj-y += side-codecs/ + +# codec drivers +obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o +obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o +obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o +obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o +obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o +obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o +obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o +obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o +obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o +obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o +obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o diff --git a/sound/pci/hda/patch_analog.c b/sound/hda/codecs/analog.c similarity index 99% rename from sound/pci/hda/patch_analog.c rename to sound/hda/codecs/analog.c index 56354fe060a1a..3557e06c6d2b3 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/hda/codecs/analog.c @@ -16,7 +16,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" struct ad198x_spec { diff --git a/sound/pci/hda/patch_ca0110.c b/sound/hda/codecs/ca0110.c similarity index 98% rename from sound/pci/hda/patch_ca0110.c rename to sound/hda/codecs/ca0110.c index 1818ce67f7613..0353544435b9a 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/hda/codecs/ca0110.c @@ -13,7 +13,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" static const struct hda_codec_ops ca0110_patch_ops = { diff --git a/sound/pci/hda/patch_ca0132.c b/sound/hda/codecs/ca0132.c similarity index 99% rename from sound/pci/hda/patch_ca0132.c rename to sound/hda/codecs/ca0132.c index 686ce0947131b..35a979465c751 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -4,7 +4,7 @@ * * Copyright (c) 2011, Creative Technology Ltd. * - * Based on patch_ca0110.c + * Based on ca0110.c * Copyright (c) 2008 Takashi Iwai */ diff --git a/sound/pci/hda/ca0132_regs.h b/sound/hda/codecs/ca0132_regs.h similarity index 100% rename from sound/pci/hda/ca0132_regs.h rename to sound/hda/codecs/ca0132_regs.h diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig new file mode 100644 index 0000000000000..f6cefb65c5f87 --- /dev/null +++ b/sound/hda/codecs/cirrus/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_HDA_CODEC_CIRRUS + tristate "Build Cirrus Logic codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include Cirrus Logic codec support in + snd-hda-intel driver, such as CS4206. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m + +config SND_HDA_CODEC_CS8409 + tristate "Build Cirrus Logic HDA bridge support" + select SND_HDA_GENERIC + help + Say Y or M here to include Cirrus Logic HDA bridge support in + snd-hda-intel driver, such as CS8409. + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m diff --git a/sound/hda/codecs/cirrus/Makefile b/sound/hda/codecs/cirrus/Makefile new file mode 100644 index 0000000000000..fa40c893fb09a --- /dev/null +++ b/sound/hda/codecs/cirrus/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-cirrus-y := cirrus.o +snd-hda-codec-cs8409-y := cs8409.o cs8409-tables.o + +obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o +obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o diff --git a/sound/pci/hda/patch_cirrus.c b/sound/hda/codecs/cirrus/cirrus.c similarity index 99% rename from sound/pci/hda/patch_cirrus.c rename to sound/hda/codecs/cirrus/cirrus.c index 06e046214a413..81ea66c4e9d31 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/hda/codecs/cirrus/cirrus.c @@ -15,7 +15,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" /* */ diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c similarity index 99% rename from sound/pci/hda/patch_cs8409-tables.c rename to sound/hda/codecs/cirrus/cs8409-tables.c index 09240138e0871..5fe49f13c0da5 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/hda/codecs/cirrus/cs8409-tables.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * patch_cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -8,7 +8,7 @@ * Author: Lucas Tanure */ -#include "patch_cs8409.h" +#include "cs8409.h" /****************************************************************************** * CS42L42 Specific Data diff --git a/sound/pci/hda/patch_cs8409.c b/sound/hda/codecs/cirrus/cs8409.c similarity index 99% rename from sound/pci/hda/patch_cs8409.c rename to sound/hda/codecs/cirrus/cs8409.c index e50006757a2c8..5ec1126b2a55a 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -13,7 +13,7 @@ #include #include -#include "patch_cs8409.h" +#include "cs8409.h" /****************************************************************************** * CS8409 Specific Functions diff --git a/sound/pci/hda/patch_cs8409.h b/sound/hda/codecs/cirrus/cs8409.h similarity index 99% rename from sound/pci/hda/patch_cs8409.h rename to sound/hda/codecs/cirrus/cs8409.h index e4bd2e12110bc..35072cd009dc2 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/hda/codecs/cirrus/cs8409.h @@ -17,7 +17,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" /* CS8409 Specific Definitions */ diff --git a/sound/pci/hda/patch_cmedia.c b/sound/hda/codecs/cmedia.c similarity index 99% rename from sound/pci/hda/patch_cmedia.c rename to sound/hda/codecs/cmedia.c index fe946d4078307..c88da2f8233ea 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/hda/codecs/cmedia.c @@ -15,7 +15,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" /* CM9825 Offset Definitions */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/hda/codecs/conexant.c similarity index 99% rename from sound/pci/hda/patch_conexant.c rename to sound/hda/codecs/conexant.c index e584a7b2877de..b7710a81fd87b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/hda/codecs/conexant.c @@ -19,7 +19,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" struct conexant_spec { struct hda_gen_spec gen; @@ -312,10 +312,10 @@ enum { }; /* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" +#include "helpers/thinkpad.c" /* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" +#include "helpers/ideapad_hotkey_led.c" static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) diff --git a/sound/pci/hda/hda_generic.c b/sound/hda/codecs/generic.c similarity index 99% rename from sound/pci/hda/hda_generic.c rename to sound/hda/codecs/generic.c index 2a28c8b6ba552..873fd4b7f4516 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/hda/codecs/generic.c @@ -25,7 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_beep.h" -#include "hda_generic.h" +#include "generic.h" /** diff --git a/sound/pci/hda/hda_generic.h b/sound/hda/codecs/generic.h similarity index 100% rename from sound/pci/hda/hda_generic.h rename to sound/hda/codecs/generic.h diff --git a/sound/hda/codecs/hdmi/Makefile b/sound/hda/codecs/hdmi/Makefile new file mode 100644 index 0000000000000..371818d4e9b20 --- /dev/null +++ b/sound/hda/codecs/hdmi/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-hdmi-y := hdmi.o eld.o + +obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o diff --git a/sound/pci/hda/hda_eld.c b/sound/hda/codecs/hdmi/eld.c similarity index 100% rename from sound/pci/hda/hda_eld.c rename to sound/hda/codecs/hdmi/eld.c diff --git a/sound/pci/hda/patch_hdmi.c b/sound/hda/codecs/hdmi/hdmi.c similarity index 99% rename from sound/pci/hda/patch_hdmi.c rename to sound/hda/codecs/hdmi/hdmi.c index 9a7793eb16e91..3811eb1dc9989 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * - * patch_hdmi.c - routines for HDMI/DisplayPort codecs + * hdmi.c - routines for HDMI/DisplayPort codecs * * Copyright(c) 2008-2010 Intel Corporation * Copyright (c) 2006 ATI Technologies Inc. diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/hda/codecs/helpers/hp_x360.c similarity index 100% rename from sound/pci/hda/hp_x360_helper.c rename to sound/hda/codecs/helpers/hp_x360.c diff --git a/sound/pci/hda/ideapad_hotkey_led_helper.c b/sound/hda/codecs/helpers/ideapad_hotkey_led.c similarity index 100% rename from sound/pci/hda/ideapad_hotkey_led_helper.c rename to sound/hda/codecs/helpers/ideapad_hotkey_led.c diff --git a/sound/pci/hda/ideapad_s740_helper.c b/sound/hda/codecs/helpers/ideapad_s740.c similarity index 100% rename from sound/pci/hda/ideapad_s740_helper.c rename to sound/hda/codecs/helpers/ideapad_s740.c diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/hda/codecs/helpers/thinkpad.c similarity index 100% rename from sound/pci/hda/thinkpad_helper.c rename to sound/hda/codecs/helpers/thinkpad.c diff --git a/sound/pci/hda/patch_realtek.c b/sound/hda/codecs/realtek.c similarity index 99% rename from sound/pci/hda/patch_realtek.c rename to sound/hda/codecs/realtek.c index 936c3470a13be..eea42bf5aec2e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/hda/codecs/realtek.c @@ -30,8 +30,8 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" -#include "hda_component.h" +#include "generic.h" +#include "side-codecs/hda_component.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 @@ -7120,7 +7120,7 @@ static void alc285_fixup_hp_beep(struct hda_codec *codec, } /* for hda_fixup_thinkpad_acpi() */ -#include "thinkpad_helper.c" +#include "helpers/thinkpad.c" static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -7130,7 +7130,7 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, } /* for hda_fixup_ideapad_acpi() */ -#include "ideapad_hotkey_led_helper.c" +#include "helpers/ideapad_hotkey_led.c" static void alc_fixup_ideapad_acpi(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -7362,10 +7362,10 @@ static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, /* for alc295_fixup_hp_top_speakers */ -#include "hp_x360_helper.c" +#include "helpers/hp_x360.c" /* for alc285_fixup_ideapad_s740_coef() */ -#include "ideapad_s740_helper.c" +#include "helpers/ideapad_s740.c" static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), diff --git a/sound/pci/hda/patch_senarytech.c b/sound/hda/codecs/senarytech.c similarity index 98% rename from sound/pci/hda/patch_senarytech.c rename to sound/hda/codecs/senarytech.c index 0691996fa971c..9a253ad19f4df 100644 --- a/sound/pci/hda/patch_senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -2,7 +2,7 @@ /* * HD audio interface patch for Senary HDA audio codec * - * Initially based on sound/pci/hda/patch_conexant.c + * Initially based on conexant.c */ #include @@ -17,7 +17,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" struct senary_spec { struct hda_gen_spec gen; diff --git a/sound/pci/hda/patch_si3054.c b/sound/hda/codecs/si3054.c similarity index 100% rename from sound/pci/hda/patch_si3054.c rename to sound/hda/codecs/si3054.c diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig new file mode 100644 index 0000000000000..cbf1847896bc9 --- /dev/null +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -0,0 +1,128 @@ +config SND_HDA_CIRRUS_SCODEC + tristate + +config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST + tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS + depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT + default KUNIT_ALL_TESTS + help + This builds KUnit tests for the cirrus side-codec library. + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + If in doubt, say "N". + +config SND_HDA_SCODEC_CS35L41 + tristate + select SND_HDA_GENERIC + select REGMAP_IRQ + select FW_CS_DSP + +config SND_HDA_SCODEC_COMPONENT + tristate + +config SND_HDA_SCODEC_CS35L41_I2C + tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L41 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m + +config SND_HDA_SCODEC_CS35L41_SPI + tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L41 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m + +config SND_HDA_SCODEC_CS35L56 + tristate + +config SND_HDA_SCODEC_CS35L56_I2C + tristate "Build CS35L56 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CIRRUS_SCODEC + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L56 amplifier support with + I2C control. + +config SND_HDA_SCODEC_CS35L56_SPI + tristate "Build CS35L56 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on SND_SOC + select FW_CS_DSP + imply SERIAL_MULTI_INSTANTIATE + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CIRRUS_SCODEC + select SND_SOC_CS_AMP_LIB + help + Say Y or M here to include CS35L56 amplifier support with + SPI control. + +config SND_HDA_SCODEC_TAS2781 + tristate + select SND_HDA_GENERIC + +config SND_HDA_SCODEC_TAS2781_I2C + tristate "Build TAS2781 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_HDA_SCODEC_TAS2781 + select SND_SOC_TAS2781_COMLIB_I2C + select SND_SOC_TAS2781_FMWLIB + select CRC32 + help + Say Y or M here to include TAS2781 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m + +config SND_HDA_SCODEC_TAS2781_SPI + tristate "Build TAS2781 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_HDA_SCODEC_TAS2781 + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + select CRC8 + select CRC32 + help + Say Y or M here to include TAS2781 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile new file mode 100644 index 0000000000000..245e84f6a121d --- /dev/null +++ b/sound/hda/codecs/side-codecs/Makefile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-cirrus-scodec-y := cirrus_scodec.o +snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o +snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o +snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o +snd-hda-scodec-cs35l56-y := cs35l56_hda.o +snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o +snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o +snd-hda-scodec-component-y := hda_component.o +snd-hda-scodec-tas2781-y := tas2781_hda.o +snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o +snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o + +obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o +obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o diff --git a/sound/pci/hda/cirrus_scodec.c b/sound/hda/codecs/side-codecs/cirrus_scodec.c similarity index 100% rename from sound/pci/hda/cirrus_scodec.c rename to sound/hda/codecs/side-codecs/cirrus_scodec.c diff --git a/sound/pci/hda/cirrus_scodec.h b/sound/hda/codecs/side-codecs/cirrus_scodec.h similarity index 100% rename from sound/pci/hda/cirrus_scodec.h rename to sound/hda/codecs/side-codecs/cirrus_scodec.h diff --git a/sound/pci/hda/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c similarity index 100% rename from sound/pci/hda/cirrus_scodec_test.c rename to sound/hda/codecs/side-codecs/cirrus_scodec_test.c diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c similarity index 99% rename from sound/pci/hda/cs35l41_hda.c rename to sound/hda/codecs/side-codecs/cs35l41_hda.c index 2d7ee121a7fd4..37f2cdc8ce824 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -17,7 +17,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "hda_component.h" #include "cs35l41_hda.h" #include "cs35l41_hda_property.h" diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h similarity index 100% rename from sound/pci/hda/cs35l41_hda.h rename to sound/hda/codecs/side-codecs/cs35l41_hda.h diff --git a/sound/pci/hda/cs35l41_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_i2c.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_property.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_property.c diff --git a/sound/pci/hda/cs35l41_hda_property.h b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h similarity index 100% rename from sound/pci/hda/cs35l41_hda_property.h rename to sound/hda/codecs/side-codecs/cs35l41_hda_property.h diff --git a/sound/pci/hda/cs35l41_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c similarity index 100% rename from sound/pci/hda/cs35l41_hda_spi.c rename to sound/hda/codecs/side-codecs/cs35l41_hda_spi.c diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c similarity index 99% rename from sound/pci/hda/cs35l56_hda.c rename to sound/hda/codecs/side-codecs/cs35l56_hda.c index e8b4995118bbf..8d43119b3dd24 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -20,7 +20,7 @@ #include "cirrus_scodec.h" #include "cs35l56_hda.h" #include "hda_component.h" -#include "hda_generic.h" +#include "../generic.h" /* * The cs35l56_hda_dai_config[] reg sequence configures the device as diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h similarity index 100% rename from sound/pci/hda/cs35l56_hda.h rename to sound/hda/codecs/side-codecs/cs35l56_hda.h diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c similarity index 100% rename from sound/pci/hda/cs35l56_hda_i2c.c rename to sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c similarity index 100% rename from sound/pci/hda/cs35l56_hda_spi.c rename to sound/hda/codecs/side-codecs/cs35l56_hda_spi.c diff --git a/sound/pci/hda/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c similarity index 100% rename from sound/pci/hda/hda_component.c rename to sound/hda/codecs/side-codecs/hda_component.c diff --git a/sound/pci/hda/hda_component.h b/sound/hda/codecs/side-codecs/hda_component.h similarity index 100% rename from sound/pci/hda/hda_component.h rename to sound/hda/codecs/side-codecs/hda_component.h diff --git a/sound/pci/hda/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c similarity index 100% rename from sound/pci/hda/tas2781_hda.c rename to sound/hda/codecs/side-codecs/tas2781_hda.c diff --git a/sound/pci/hda/tas2781_hda.h b/sound/hda/codecs/side-codecs/tas2781_hda.h similarity index 100% rename from sound/pci/hda/tas2781_hda.h rename to sound/hda/codecs/side-codecs/tas2781_hda.h diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c similarity index 99% rename from sound/pci/hda/tas2781_hda_i2c.c rename to sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index b7ee22840d789..bacc3f6ed4bd9 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -30,7 +30,7 @@ #include "hda_auto_parser.h" #include "hda_component.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "tas2781_hda.h" #define TAS2563_CAL_VAR_NAME_MAX 16 diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c similarity index 99% rename from sound/pci/hda/tas2781_hda_spi.c rename to sound/hda/codecs/side-codecs/tas2781_hda_spi.c index c4b9a3c1a7f07..09a5d0f131b2f 100644 --- a/sound/pci/hda/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -35,7 +35,7 @@ #include "hda_auto_parser.h" #include "hda_component.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "../generic.h" #include "tas2781_hda.h" #define TASDEVICE_RANGE_MAX_SIZE (256 * 128) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/hda/codecs/sigmatel.c similarity index 99% rename from sound/pci/hda/patch_sigmatel.c rename to sound/hda/codecs/sigmatel.c index bde6b73738583..56274ff49b0b7 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -7,7 +7,7 @@ * Copyright (c) 2005 Embedded Alley Solutions, Inc. * Matt Porter * - * Based on patch_cmedia.c and patch_realtek.c + * Based on cmedia.c and realtek.c * Copyright (c) 2004 Takashi Iwai */ @@ -24,7 +24,7 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" enum { STAC_REF, diff --git a/sound/pci/hda/patch_via.c b/sound/hda/codecs/via.c similarity index 99% rename from sound/pci/hda/patch_via.c rename to sound/hda/codecs/via.c index d0893059b1b9b..e3ce563f866b3 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/hda/codecs/via.c @@ -43,7 +43,7 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include "generic.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 787868c9e91b0..e0996a9d90b03 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -933,5 +933,3 @@ config SND_YMFPCI will be called snd-ymfpci. endif # SND_PCI - -source "sound/pci/hda/Kconfig" diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 18b673018dfd6..9d5e8e12ae738 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_SND) += \ lx6464es/ \ echoaudio/ \ emu10k1/ \ - hda/ \ ice1712/ \ korg1212/ \ mixart/ \ diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile deleted file mode 100644 index 79de0af71ad46..0000000000000 --- a/sound/pci/hda/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -subdir-ccflags-y += -I$(src)/../../hda/common - -snd-hda-codec-generic-y := hda_generic.o -snd-hda-codec-realtek-y := patch_realtek.o -snd-hda-codec-cmedia-y := patch_cmedia.o -snd-hda-codec-analog-y := patch_analog.o -snd-hda-codec-idt-y := patch_sigmatel.o -snd-hda-codec-si3054-y := patch_si3054.o -snd-hda-codec-cirrus-y := patch_cirrus.o -snd-hda-codec-cs8409-y := patch_cs8409.o patch_cs8409-tables.o -snd-hda-codec-ca0110-y := patch_ca0110.o -snd-hda-codec-ca0132-y := patch_ca0132.o -snd-hda-codec-conexant-y := patch_conexant.o -snd-hda-codec-senarytech-y :=patch_senarytech.o -snd-hda-codec-via-y := patch_via.o -snd-hda-codec-hdmi-y := patch_hdmi.o hda_eld.o - -# side codecs -snd-hda-cirrus-scodec-y := cirrus_scodec.o -snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o -snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o -snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o -snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o -snd-hda-scodec-cs35l56-y := cs35l56_hda.o -snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o -snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o -snd-hda-scodec-component-y := hda_component.o -snd-hda-scodec-tas2781-y := tas2781_hda.o -snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o -snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o - -# codec drivers -obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o -obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o -obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o -obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o -obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o -obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o -obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o -obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o -obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o -obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o -obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o -obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o -obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o -obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o - -# side codecs -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o -obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o -obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o -obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o -- GitLab From aeeb85f26c3bbef6f702ac20167c45812251501d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:10 +0200 Subject: [PATCH 638/868] ALSA: hda: Split Realtek HD-audio codec driver The snd-hda-codec-realtek driver supports many different codec models and accumulated lots of quirks. Now let's split it to multiple modules per probe function, i.e. for ALC260, ALC262, ALC269, etc. The common code and quirks are provided by the common library module, snd-hda-codec-realtek-lib now. One drawback of this action is that many symbols have to be exported. But they are limited with SND_HDA_CODEC_REALTEK namespace, at least. This patch tries to be idiomatic and doesn't try to rewrite the existing code. We can move the codec model-specific code into each codec driver later. The HD-audio sub-codec component binding is currently specific to ALC269, hence the management is moved into alc269.c. After that, alc_free() became identical with snd_hda_gen_free(), and it's replaced as a macro just to call snd_hda_gen_free(). Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-8-tiwai@suse.de --- sound/hda/codecs/Kconfig | 14 +- sound/hda/codecs/Makefile | 5 +- sound/hda/codecs/realtek/Kconfig | 90 + sound/hda/codecs/realtek/Makefile | 26 + sound/hda/codecs/realtek/alc260.c | 276 + sound/hda/codecs/realtek/alc262.c | 199 + sound/hda/codecs/realtek/alc268.c | 176 + .../codecs/{realtek.c => realtek/alc269.c} | 19935 ++++++---------- sound/hda/codecs/realtek/alc662.c | 1102 + sound/hda/codecs/realtek/alc680.c | 53 + sound/hda/codecs/realtek/alc861.c | 149 + sound/hda/codecs/realtek/alc861vd.c | 123 + sound/hda/codecs/realtek/alc880.c | 497 + sound/hda/codecs/realtek/alc882.c | 847 + sound/hda/codecs/realtek/realtek.c | 2314 ++ sound/hda/codecs/realtek/realtek.h | 302 + 16 files changed, 13295 insertions(+), 12813 deletions(-) create mode 100644 sound/hda/codecs/realtek/Kconfig create mode 100644 sound/hda/codecs/realtek/Makefile create mode 100644 sound/hda/codecs/realtek/alc260.c create mode 100644 sound/hda/codecs/realtek/alc262.c create mode 100644 sound/hda/codecs/realtek/alc268.c rename sound/hda/codecs/{realtek.c => realtek/alc269.c} (64%) create mode 100644 sound/hda/codecs/realtek/alc662.c create mode 100644 sound/hda/codecs/realtek/alc680.c create mode 100644 sound/hda/codecs/realtek/alc861.c create mode 100644 sound/hda/codecs/realtek/alc861vd.c create mode 100644 sound/hda/codecs/realtek/alc880.c create mode 100644 sound/hda/codecs/realtek/alc882.c create mode 100644 sound/hda/codecs/realtek/realtek.c create mode 100644 sound/hda/codecs/realtek/realtek.h diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig index bac19a664be0f..0bb675ab84fe5 100644 --- a/sound/hda/codecs/Kconfig +++ b/sound/hda/codecs/Kconfig @@ -4,19 +4,6 @@ if SND_HDA config SND_HDA_GENERIC_LEDS bool -config SND_HDA_CODEC_REALTEK - tristate "Build Realtek HD-audio codec support" - depends on INPUT - select SND_HDA_GENERIC - select SND_HDA_GENERIC_LEDS - select SND_HDA_SCODEC_COMPONENT - help - Say Y or M here to include Realtek HD-audio codec support in - snd-hda-intel driver, such as ALC880. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m - config SND_HDA_CODEC_ANALOG tristate "Build Analog Devices HD-audio codec support" select SND_HDA_GENERIC @@ -162,6 +149,7 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM This feature can impact power consumption as resources are kept reserved both at transmitter and receiver. +source "sound/hda/codecs/realtek/Kconfig" source "sound/hda/codecs/cirrus/Kconfig" source "sound/hda/codecs/side-codecs/Kconfig" diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile index 205cd1373b421..14e5041aa4f07 100644 --- a/sound/hda/codecs/Makefile +++ b/sound/hda/codecs/Makefile @@ -2,30 +2,31 @@ subdir-ccflags-y += -I$(src)/../common snd-hda-codec-generic-y := generic.o +snd-hda-codec-cmedia-y := cmedia.o snd-hda-codec-analog-y := analog.o snd-hda-codec-ca0110-y := ca0110.o snd-hda-codec-ca0132-y := ca0132.o snd-hda-codec-cmedia-y := cmedia.o snd-hda-codec-conexant-y := conexant.o snd-hda-codec-idt-y := sigmatel.o -snd-hda-codec-realtek-y := realtek.o snd-hda-codec-senarytech-y := senarytech.o snd-hda-codec-si3054-y := si3054.o snd-hda-codec-via-y := via.o obj-y += cirrus/ obj-y += hdmi/ +obj-y += realtek/ obj-y += side-codecs/ # codec drivers obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o +obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o -obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o diff --git a/sound/hda/codecs/realtek/Kconfig b/sound/hda/codecs/realtek/Kconfig new file mode 100644 index 0000000000000..4b3ab28203b47 --- /dev/null +++ b/sound/hda/codecs/realtek/Kconfig @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig SND_HDA_CODEC_REALTEK + bool "Realtek HD-audio codec support" + +if SND_HDA_CODEC_REALTEK + +config SND_HDA_CODEC_REALTEK_LIB + tristate + select SND_HDA_GENERIC + select SND_HDA_GENERIC_LEDS + select SND_HDA_SCODEC_COMPONENT + +config SND_HDA_CODEC_ALC260 + tristate "Build Realtek ALC260 HD-audio codec support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC260 HD-audio codec support + +config SND_HDA_CODEC_ALC262 + tristate "Build Realtek ALC262 HD-audio codec support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC262 HD-audio codec support + +config SND_HDA_CODEC_ALC268 + tristate "Build Realtek ALC268 HD-audio codec support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC268 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC269 + tristate "Build Realtek ALC269 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC269 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC662 + tristate "Build Realtek ALC662 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC662 and compatible HD-audio + codec support + +config SND_HDA_CODEC_ALC680 + tristate "Build Realtek ALC680 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC680 HD-audio codec support + +config SND_HDA_CODEC_ALC861 + tristate "Build Realtek ALC861 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC861 HD-audio codec support + +config SND_HDA_CODEC_ALC861VD + tristate "Build Realtek ALC861-VD HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC861-VD HD-audio codec support + +config SND_HDA_CODEC_ALC880 + tristate "Build Realtek ALC880 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC880 HD-audio codec support + +config SND_HDA_CODEC_ALC882 + tristate "Build Realtek ALC882 HD-audio codecs support" + depends on INPUT + select SND_HDA_CODEC_REALTEK_LIB + help + Say Y or M here to include Realtek ALC882 and compatible HD-audio + codec support + +endif + + diff --git a/sound/hda/codecs/realtek/Makefile b/sound/hda/codecs/realtek/Makefile new file mode 100644 index 0000000000000..c6ee4e526a403 --- /dev/null +++ b/sound/hda/codecs/realtek/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +subdir-ccflags-y += -I$(src)/../../common + +snd-hda-codec-realtek-lib-y := realtek.o +snd-hda-codec-alc260-y := alc260.o +snd-hda-codec-alc262-y := alc262.o +snd-hda-codec-alc268-y := alc268.o +snd-hda-codec-alc269-y := alc269.o +snd-hda-codec-alc662-y := alc662.o +snd-hda-codec-alc680-y := alc680.o +snd-hda-codec-alc861-y := alc861.o +snd-hda-codec-alc861vd-y := alc861vd.o +snd-hda-codec-alc880-y := alc880.o +snd-hda-codec-alc882-y := alc882.o + +obj-$(CONFIG_SND_HDA_CODEC_REALTEK_LIB) += snd-hda-codec-realtek-lib.o +obj-$(CONFIG_SND_HDA_CODEC_ALC260) += snd-hda-codec-alc260.o +obj-$(CONFIG_SND_HDA_CODEC_ALC262) += snd-hda-codec-alc262.o +obj-$(CONFIG_SND_HDA_CODEC_ALC268) += snd-hda-codec-alc268.o +obj-$(CONFIG_SND_HDA_CODEC_ALC269) += snd-hda-codec-alc269.o +obj-$(CONFIG_SND_HDA_CODEC_ALC662) += snd-hda-codec-alc662.o +obj-$(CONFIG_SND_HDA_CODEC_ALC680) += snd-hda-codec-alc680.o +obj-$(CONFIG_SND_HDA_CODEC_ALC861) += snd-hda-codec-alc861.o +obj-$(CONFIG_SND_HDA_CODEC_ALC861VD) += snd-hda-codec-alc861vd.o +obj-$(CONFIG_SND_HDA_CODEC_ALC880) += snd-hda-codec-alc880.o +obj-$(CONFIG_SND_HDA_CODEC_ALC882) += snd-hda-codec-alc882.o diff --git a/sound/hda/codecs/realtek/alc260.c b/sound/hda/codecs/realtek/alc260.c new file mode 100644 index 0000000000000..ebe20eaec58a4 --- /dev/null +++ b/sound/hda/codecs/realtek/alc260.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC260 codec +// + +#include +#include +#include "realtek.h" + +static int alc260_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; + static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; + return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); +} + +/* + * Pin config fixes + */ +enum { + ALC260_FIXUP_HP_DC5750, + ALC260_FIXUP_HP_PIN_0F, + ALC260_FIXUP_COEF, + ALC260_FIXUP_GPIO1, + ALC260_FIXUP_GPIO1_TOGGLE, + ALC260_FIXUP_REPLACER, + ALC260_FIXUP_HP_B1900, + ALC260_FIXUP_KN1, + ALC260_FIXUP_FSC_S7020, + ALC260_FIXUP_FSC_S7020_JWSE, + ALC260_FIXUP_VAIO_PINS, +}; + +static void alc260_gpio1_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); +} + +static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PROBE) { + /* although the machine has only one output pin, we need to + * toggle GPIO1 according to the jack state + */ + spec->gen.automute_hook = alc260_gpio1_automute; + spec->gen.detect_hp = 1; + spec->gen.automute_speaker = 1; + spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable_callback(codec, 0x0f, + snd_hda_gen_hp_automute); + alc_setup_gpio(codec, 0x01); + } +} + +static void alc260_fixup_kn1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x0f, 0x02214000 }, /* HP/speaker */ + { 0x12, 0x90a60160 }, /* int mic */ + { 0x13, 0x02a19000 }, /* ext mic */ + { 0x18, 0x01446000 }, /* SPDIF out */ + /* disable bogus I/O pins */ + { 0x10, 0x411111f0 }, + { 0x11, 0x411111f0 }, + { 0x14, 0x411111f0 }, + { 0x15, 0x411111f0 }, + { 0x16, 0x411111f0 }, + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + spec->init_amp = ALC_INIT_NONE; + break; + } +} + +static void alc260_fixup_fsc_s7020(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->init_amp = ALC_INIT_NONE; +} + +static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.add_jack_modes = 1; + spec->gen.hp_mic = 1; + } +} + +static const struct hda_fixup alc260_fixups[] = { + [ALC260_FIXUP_HP_DC5750] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x11, 0x90130110 }, /* speaker */ + { } + } + }, + [ALC260_FIXUP_HP_PIN_0F] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0f, 0x01214000 }, /* HP */ + { } + } + }, + [ALC260_FIXUP_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, + { } + }, + }, + [ALC260_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC260_FIXUP_GPIO1_TOGGLE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, + [ALC260_FIXUP_REPLACER] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, + }, + [ALC260_FIXUP_HP_B1900] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_COEF, + }, + [ALC260_FIXUP_KN1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_kn1, + }, + [ALC260_FIXUP_FSC_S7020] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020, + }, + [ALC260_FIXUP_FSC_S7020_JWSE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020_jwse, + .chained = true, + .chain_id = ALC260_FIXUP_FSC_S7020, + }, + [ALC260_FIXUP_VAIO_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* Pin configs are missing completely on some VAIOs */ + { 0x0f, 0x01211020 }, + { 0x10, 0x0001003f }, + { 0x11, 0x411111f0 }, + { 0x12, 0x01a15930 }, + { 0x13, 0x411111f0 }, + { 0x14, 0x411111f0 }, + { 0x15, 0x411111f0 }, + { 0x16, 0x411111f0 }, + { 0x17, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { } + } + }, +}; + +static const struct hda_quirk alc260_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), + SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), + SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), + SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), + SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), + SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), + {} +}; + +static const struct hda_model_fixup alc260_fixup_models[] = { + {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC260_FIXUP_COEF, .name = "coef"}, + {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, + {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, + {} +}; + +/* + */ +static int patch_alc260(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x07); + if (err < 0) + return err; + + spec = codec->spec; + /* as quite a few machines require HP amp for speaker outputs, + * it's easier to enable it unconditionally; even if it's unneeded, + * it's almost harmless. + */ + spec->gen.prefer_hp_amp = 1; + spec->gen.beep_nid = 0x01; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, + alc260_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc260_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc260[] = { + HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc260_driver = { + .id = snd_hda_id_alc260, +}; + +module_hda_codec_driver(alc260_driver); diff --git a/sound/hda/codecs/realtek/alc262.c b/sound/hda/codecs/realtek/alc262.c new file mode 100644 index 0000000000000..ffe61f447667f --- /dev/null +++ b/sound/hda/codecs/realtek/alc262.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC262 codec +// + +#include +#include +#include "realtek.h" + +static int alc262_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); +} + +/* + * Pin config fixes + */ +enum { + ALC262_FIXUP_FSC_H270, + ALC262_FIXUP_FSC_S7110, + ALC262_FIXUP_HP_Z200, + ALC262_FIXUP_TYAN, + ALC262_FIXUP_LENOVO_3000, + ALC262_FIXUP_BENQ, + ALC262_FIXUP_BENQ_T31, + ALC262_FIXUP_INV_DMIC, + ALC262_FIXUP_INTEL_BAYLEYBAY, +}; + +static const struct hda_fixup alc262_fixups[] = { + [ALC262_FIXUP_FSC_H270] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0221142f }, /* front HP */ + { 0x1b, 0x0121141f }, /* rear HP */ + { } + } + }, + [ALC262_FIXUP_FSC_S7110] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x90170110 }, /* speaker */ + { } + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, + [ALC262_FIXUP_HP_Z200] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130120 }, /* internal speaker */ + { } + } + }, + [ALC262_FIXUP_TYAN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x1993e1f0 }, /* int AUX */ + { } + } + }, + [ALC262_FIXUP_LENOVO_3000] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, + {} + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, + [ALC262_FIXUP_BENQ] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + } + }, + [ALC262_FIXUP_BENQ_T31] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + {} + } + }, + [ALC262_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC262_FIXUP_INTEL_BAYLEYBAY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_depop_delay, + }, +}; + +static const struct hda_quirk alc262_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), + SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), + SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270), + SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), + SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), + SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY), + {} +}; + +static const struct hda_model_fixup alc262_fixup_models[] = { + {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, + {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, + {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, + {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, + {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, + {.id = ALC262_FIXUP_BENQ, .name = "benq"}, + {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, + {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, + {} +}; + +/* + */ +static int patch_alc262(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + spec->gen.shared_mic_vref_pin = 0x18; + + spec->shutup = alc_eapd_shutup; + +#if 0 + /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is + * under-run + */ + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); +#endif + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + alc262_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + /* automatic parse from the BIOS config */ + err = alc262_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc262[] = { + HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc262); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC262 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc262_driver = { + .id = snd_hda_id_alc262, +}; + +module_hda_codec_driver(alc262_driver); diff --git a/sound/hda/codecs/realtek/alc268.c b/sound/hda/codecs/realtek/alc268.c new file mode 100644 index 0000000000000..d018f9982feb3 --- /dev/null +++ b/sound/hda/codecs/realtek/alc268.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "realtek.h" + +/* bind Beep switches of both NID 0x0f and 0x10 */ +static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned long pval; + int err; + + mutex_lock(&codec->control_mutex); + pval = kcontrol->private_value; + kcontrol->private_value = (pval & ~0xff) | 0x0f; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err >= 0) { + kcontrol->private_value = (pval & ~0xff) | 0x10; + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + } + kcontrol->private_value = pval; + mutex_unlock(&codec->control_mutex); + return err; +} + +static const struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Switch", + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_beep_switch_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) + }, +}; + +/* set PCBEEP vol = 0, mute connections */ +static const struct hda_verb alc268_beep_init_verbs[] = { + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } +}; + +enum { + ALC268_FIXUP_INV_DMIC, + ALC268_FIXUP_HP_EAPD, + ALC268_FIXUP_SPDIF, +}; + +static const struct hda_fixup alc268_fixups[] = { + [ALC268_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC268_FIXUP_HP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC268_FIXUP_SPDIF] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x014b1180 }, /* enable SPDIF out */ + {} + } + }, +}; + +static const struct hda_model_fixup alc268_fixup_models[] = { + {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, + {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, + {} +}; + +static const struct hda_quirk alc268_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), + SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), + /* below is codec SSID since multiple Toshiba laptops have the + * same PCI SSID 1179:ff00 + */ + SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), + {} +}; + +/* + * BIOS auto configuration + */ +static int alc268_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, NULL, alc268_ssids); +} + +/* + */ +static int patch_alc268(struct hda_codec *codec) +{ + struct alc_spec *spec; + int i, err; + + /* ALC268 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc268_parse_auto_config(codec); + if (err < 0) + goto error; + + if (err > 0 && !spec->gen.no_analog && + spec->gen.autocfg.speaker_pins[0] != 0x1d) { + for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc268_beep_mixer[i])) { + err = -ENOMEM; + goto error; + } + } + snd_hda_add_verbs(codec, alc268_beep_init_verbs); + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc268[] = { + HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), + HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC267/268 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc268_driver = { + .id = snd_hda_id_alc268, +}; + +module_hda_codec_driver(alc268_driver); diff --git a/sound/hda/codecs/realtek.c b/sound/hda/codecs/realtek/alc269.c similarity index 64% rename from sound/hda/codecs/realtek.c rename to sound/hda/codecs/realtek/alc269.c index eea42bf5aec2e..13549b5b9c428 100644 --- a/sound/hda/codecs/realtek.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -1,13596 +1,7860 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* - * Universal Interface for Intel High Definition Audio Codec - * - * HD audio interface patch for Realtek ALC codecs - * - * Copyright (c) 2004 Kailang Yang - * PeiSen Hou - * Takashi Iwai - * Jonathan Woithe - */ +// +// Realtek ALC269 and compatible codecs +// -#include -#include #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hda_local.h" -#include "hda_auto_parser.h" -#include "hda_beep.h" -#include "hda_jack.h" -#include "generic.h" -#include "side-codecs/hda_component.h" +#include "realtek.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 -/* extra amp-initialization sequence types */ -enum { - ALC_INIT_UNDEFINED, - ALC_INIT_NONE, - ALC_INIT_DEFAULT, -}; - -enum { - ALC_HEADSET_MODE_UNKNOWN, - ALC_HEADSET_MODE_UNPLUGGED, - ALC_HEADSET_MODE_HEADSET, - ALC_HEADSET_MODE_MIC, - ALC_HEADSET_MODE_HEADPHONE, +static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ }; -enum { - ALC_HEADSET_TYPE_UNKNOWN, - ALC_HEADSET_TYPE_CTIA, - ALC_HEADSET_TYPE_OMTP, +static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { + .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ }; +/* different alc269-variants */ enum { - ALC_KEY_MICMUTE_INDEX, -}; - -struct alc_customize_define { - unsigned int sku_cfg; - unsigned char port_connectivity; - unsigned char check_sum; - unsigned char customization; - unsigned char external_amp; - unsigned int enable_pcbeep:1; - unsigned int platform_type:1; - unsigned int swap:1; - unsigned int override:1; - unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ -}; - -struct alc_coef_led { - unsigned int idx; - unsigned int mask; - unsigned int on; - unsigned int off; -}; - -struct alc_spec { - struct hda_gen_spec gen; /* must be at head */ - - /* codec parameterization */ - struct alc_customize_define cdefine; - unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ - - /* GPIO bits */ - unsigned int gpio_mask; - unsigned int gpio_dir; - unsigned int gpio_data; - bool gpio_write_delay; /* add a delay before writing gpio_data */ - - /* mute LED for HP laptops, see vref_mute_led_set() */ - int mute_led_polarity; - int micmute_led_polarity; - hda_nid_t mute_led_nid; - hda_nid_t cap_mute_led_nid; - - unsigned int gpio_mute_led_mask; - unsigned int gpio_mic_led_mask; - struct alc_coef_led mute_led_coef; - struct alc_coef_led mic_led_coef; - struct mutex coef_mutex; - - hda_nid_t headset_mic_pin; - hda_nid_t headphone_mic_pin; - int current_headset_mode; - int current_headset_type; - - /* hooks */ - void (*init_hook)(struct hda_codec *codec); - void (*power_hook)(struct hda_codec *codec); - void (*shutup)(struct hda_codec *codec); - - int init_amp; - int codec_variant; /* flag for other variants */ - unsigned int has_alc5505_dsp:1; - unsigned int no_depop_delay:1; - unsigned int done_hp_init:1; - unsigned int no_shutup_pins:1; - unsigned int ultra_low_power:1; - unsigned int has_hs_key:1; - unsigned int no_internal_mic_pin:1; - unsigned int en_3kpull_low:1; - int num_speaker_amps; - - /* for PLL fix */ - hda_nid_t pll_nid; - unsigned int pll_coef_idx, pll_coef_bit; - unsigned int coef0; - struct input_dev *kb_dev; - u8 alc_mute_keycode_map[1]; - - /* component binding */ - struct hda_component_parent comps; + ALC269_TYPE_ALC269VA, + ALC269_TYPE_ALC269VB, + ALC269_TYPE_ALC269VC, + ALC269_TYPE_ALC269VD, + ALC269_TYPE_ALC280, + ALC269_TYPE_ALC282, + ALC269_TYPE_ALC283, + ALC269_TYPE_ALC284, + ALC269_TYPE_ALC293, + ALC269_TYPE_ALC286, + ALC269_TYPE_ALC298, + ALC269_TYPE_ALC255, + ALC269_TYPE_ALC256, + ALC269_TYPE_ALC257, + ALC269_TYPE_ALC215, + ALC269_TYPE_ALC225, + ALC269_TYPE_ALC245, + ALC269_TYPE_ALC287, + ALC269_TYPE_ALC294, + ALC269_TYPE_ALC300, + ALC269_TYPE_ALC623, + ALC269_TYPE_ALC700, }; /* - * COEF access helper functions + * BIOS auto configuration */ - -static void coef_mutex_lock(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_power_up_pm(codec); - mutex_lock(&spec->coef_mutex); -} - -static void coef_mutex_unlock(struct hda_codec *codec) +static int alc269_parse_auto_config(struct hda_codec *codec) { + static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; + const hda_nid_t *ssids; - mutex_unlock(&spec->coef_mutex); - snd_hda_power_down_pm(codec); -} - -static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - return val; -} - -static int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx) -{ - unsigned int val; + switch (spec->codec_variant) { + case ALC269_TYPE_ALC269VA: + case ALC269_TYPE_ALC269VC: + case ALC269_TYPE_ALC280: + case ALC269_TYPE_ALC284: + case ALC269_TYPE_ALC293: + ssids = alc269va_ssids; + break; + case ALC269_TYPE_ALC269VB: + case ALC269_TYPE_ALC269VD: + case ALC269_TYPE_ALC282: + case ALC269_TYPE_ALC283: + case ALC269_TYPE_ALC286: + case ALC269_TYPE_ALC298: + case ALC269_TYPE_ALC255: + case ALC269_TYPE_ALC256: + case ALC269_TYPE_ALC257: + case ALC269_TYPE_ALC215: + case ALC269_TYPE_ALC225: + case ALC269_TYPE_ALC245: + case ALC269_TYPE_ALC287: + case ALC269_TYPE_ALC294: + case ALC269_TYPE_ALC300: + case ALC269_TYPE_ALC623: + case ALC269_TYPE_ALC700: + ssids = alc269_ssids; + break; + default: + ssids = alc269_ssids; + break; + } - coef_mutex_lock(codec); - val = __alc_read_coefex_idx(codec, nid, coef_idx); - coef_mutex_unlock(codec); - return val; + return alc_parse_auto_config(codec, alc269_ignore, ssids); } -#define alc_read_coef_idx(codec, coef_idx) \ - alc_read_coefex_idx(codec, 0x20, coef_idx) +static const struct hda_jack_keymap alc_headset_btn_keymap[] = { + { SND_JACK_BTN_0, KEY_PLAYPAUSE }, + { SND_JACK_BTN_1, KEY_VOICECOMMAND }, + { SND_JACK_BTN_2, KEY_VOLUMEUP }, + { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, + {} +}; -static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) +static void alc_headset_btn_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); -} + int report = 0; -static void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_val) -{ - coef_mutex_lock(codec); - __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); - coef_mutex_unlock(codec); -} + if (jack->unsol_res & (7 << 13)) + report |= SND_JACK_BTN_0; -#define alc_write_coef_idx(codec, coef_idx, coef_val) \ - alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) + if (jack->unsol_res & (1 << 16 | 3 << 8)) + report |= SND_JACK_BTN_1; -static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); + /* Volume up key */ + if (jack->unsol_res & (7 << 23)) + report |= SND_JACK_BTN_2; - if (val != -1) - __alc_write_coefex_idx(codec, nid, coef_idx, - (val & ~mask) | bits_set); -} + /* Volume down key */ + if (jack->unsol_res & (7 << 10)) + report |= SND_JACK_BTN_3; -static void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int mask, - unsigned int bits_set) -{ - coef_mutex_lock(codec); - __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); - coef_mutex_unlock(codec); + snd_hda_jack_set_button_state(codec, jack->nid, report); } -#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ - alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) - -/* a special bypass for COEF 0; read the cached value at the second time */ -static unsigned int alc_get_coef0(struct hda_codec *codec) +static void alc_disable_headset_jack_key(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!spec->coef0) - spec->coef0 = alc_read_coef_idx(codec, 0); - return spec->coef0; -} - -/* coef writes/updates batch */ -struct coef_fw { - unsigned char nid; - unsigned char idx; - unsigned short mask; - unsigned short val; -}; - -#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ - { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } -#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) -#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) -#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) + if (!spec->has_hs_key) + return; -static void alc_process_coef_fw(struct hda_codec *codec, - const struct coef_fw *fw) -{ - coef_mutex_lock(codec); - for (; fw->nid; fw++) { - if (fw->mask == (unsigned short)-1) - __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); - else - __alc_update_coefex_idx(codec, fw->nid, fw->idx, - fw->mask, fw->val); + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0287: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0x0); + alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); + alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x10ec0257: + case 0x19e58326: + alc_write_coef_idx(codec, 0x48, 0x0); + alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); + break; } - coef_mutex_unlock(codec); -} - -/* - * GPIO setup tables, used in initialization - */ - -/* Enable GPIO mask and set output */ -static void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) -{ - struct alc_spec *spec = codec->spec; - - spec->gpio_mask |= mask; - spec->gpio_dir |= mask; - spec->gpio_data |= mask; -} - -static void alc_write_gpio_data(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); -} - -static void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, - bool on) -{ - struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_data; - - if (on) - spec->gpio_data |= mask; - else - spec->gpio_data &= ~mask; - if (oldval != spec->gpio_data) - alc_write_gpio_data(codec); } -static void alc_write_gpio(struct hda_codec *codec) +static void alc_enable_headset_jack_key(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!spec->gpio_mask) + if (!spec->has_hs_key) return; - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); - if (spec->gpio_write_delay) - msleep(1); - alc_write_gpio_data(codec); -} - -static void alc_fixup_gpio(struct hda_codec *codec, int action, - unsigned int mask) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - alc_setup_gpio(codec, mask); -} - -static void alc_fixup_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x01); + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0287: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x10ec0257: + case 0x19e58326: + alc_write_coef_idx(codec, 0x48, 0xd011); + alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); + break; + } } -static void alc_fixup_gpio2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc_fixup_headset_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - alc_fixup_gpio(codec, action, 0x02); -} + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin; -static void alc_fixup_gpio3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x03); -} + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->has_hs_key = 1; + snd_hda_jack_detect_enable_callback(codec, 0x55, + alc_headset_btn_callback); + break; + case HDA_FIXUP_ACT_BUILD: + hp_pin = alc_get_hp_pin(spec); + if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55, + alc_headset_btn_keymap, + hp_pin)) + snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", + false, SND_JACK_HEADSET, + alc_headset_btn_keymap); -static void alc_fixup_gpio4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_gpio(codec, action, 0x04); + alc_enable_headset_jack_key(codec); + break; + } } -static void alc_fixup_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_gen_add_micmute_led_cdev(codec, NULL); + alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0); } -/* - * Fix hardware PLL issue - * On some codecs, the analog PLL gating control must be off while - * the default value is 1. - */ -static void alc_fix_pll(struct hda_codec *codec) +static void alc269_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->pll_nid) - alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, - 1 << spec->pll_coef_bit, 0); -} - -static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, - unsigned int coef_idx, unsigned int coef_bit) -{ - struct alc_spec *spec = codec->spec; - spec->pll_nid = nid; - spec->pll_coef_idx = coef_idx; - spec->pll_coef_bit = coef_bit; - alc_fix_pll(codec); + if (spec->codec_variant == ALC269_TYPE_ALC269VB) + alc269vb_toggle_power_output(codec, 0); + if (spec->codec_variant == ALC269_TYPE_ALC269VB && + (alc_get_coef0(codec) & 0x00ff) == 0x018) { + msleep(150); + } + alc_shutup_pins(codec); } -/* update the master volume per volume-knob's unsol event */ -static void alc_update_knob_master(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - unsigned int val; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl; - - kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); - if (!kctl) - return; - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return; - val = snd_hda_codec_read(codec, jack->nid, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); - val &= HDA_AMP_VOLMASK; - uctl->value.integer.value[0] = val; - uctl->value.integer.value[1] = val; - kctl->put(kctl, uctl); - kfree(uctl); -} +static const struct coef_fw alc282_coefs[] = { + WRITE_COEF(0x03, 0x0002), /* Power Down Control */ + UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ + WRITE_COEF(0x07, 0x0200), /* DMIC control */ + UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ + UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ + WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ + WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ + WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */ + UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ + UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ + WRITE_COEF(0x6f, 0x0), /* Class D test 4 */ + UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */ + WRITE_COEF(0x34, 0xa0c0), /* ANC */ + UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */ + UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ + UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ + WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ + WRITE_COEF(0x63, 0x2902), /* PLL */ + WRITE_COEF(0x68, 0xa080), /* capless control 2 */ + WRITE_COEF(0x69, 0x3400), /* capless control 3 */ + WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */ + WRITE_COEF(0x6b, 0x0), /* capless control 5 */ + UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */ + WRITE_COEF(0x6e, 0x110a), /* class D test 3 */ + UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */ + WRITE_COEF(0x71, 0x0014), /* class D test 6 */ + WRITE_COEF(0x72, 0xc2ba), /* classD OCP */ + UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */ + WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */ + {} +}; -static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) +static void alc282_restore_default_value(struct hda_codec *codec) { - /* For some reason, the res given from ALC880 is broken. - Here we adjust it properly. */ - snd_hda_jack_unsol_event(codec, res >> 2); + alc_process_coef_fw(codec, alc282_coefs); } -/* Change EAPD to verb control */ -static void alc_fill_eapd_coef(struct hda_codec *codec) +static void alc282_init(struct hda_codec *codec) { - int coef; - - coef = alc_get_coef0(codec); - - switch (codec->core.vendor_id) { - case 0x10ec0262: - alc_update_coef_idx(codec, 0x7, 0, 1<<5); - break; - case 0x10ec0267: - case 0x10ec0268: - alc_update_coef_idx(codec, 0x7, 0, 1<<13); - break; - case 0x10ec0269: - if ((coef & 0x00f0) == 0x0010) - alc_update_coef_idx(codec, 0xd, 0, 1<<14); - if ((coef & 0x00f0) == 0x0020) - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0280: - case 0x10ec0284: - case 0x10ec0290: - case 0x10ec0292: - alc_update_coef_idx(codec, 0x4, 1<<15, 0); - break; - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - fallthrough; - case 0x10ec0215: - case 0x10ec0236: - case 0x10ec0245: - case 0x10ec0256: - case 0x10ec0257: - case 0x10ec0285: - case 0x10ec0289: - alc_update_coef_idx(codec, 0x36, 1<<13, 0); - fallthrough; - case 0x10ec0230: - case 0x10ec0233: - case 0x10ec0235: - case 0x10ec0255: - case 0x19e58326: - case 0x10ec0282: - case 0x10ec0283: - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - case 0x10ec0300: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - break; - case 0x10ec0275: - alc_update_coef_idx(codec, 0xe, 0, 1<<0); - break; - case 0x10ec0287: - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - alc_write_coef_idx(codec, 0x8, 0x4ab7); - break; - case 0x10ec0293: - alc_update_coef_idx(codec, 0xa, 1<<13, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - alc_write_coef_idx(codec, 0x6e, 0x0c25); - fallthrough; - case 0x10ec0294: - case 0x10ec0700: - case 0x10ec0701: - case 0x10ec0703: - case 0x10ec0711: - alc_update_coef_idx(codec, 0x10, 1<<15, 0); - break; - case 0x10ec0662: - if ((coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ - break; - case 0x10ec0272: - case 0x10ec0273: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0670: - case 0x10ec0671: - case 0x10ec0672: - alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ - break; - case 0x10ec0222: - case 0x10ec0623: - alc_update_coef_idx(codec, 0x19, 1<<13, 0); - break; - case 0x10ec0668: - alc_update_coef_idx(codec, 0x7, 3<<13, 0); - break; - case 0x10ec0867: - alc_update_coef_idx(codec, 0x4, 1<<10, 0); - break; - case 0x10ec0888: - if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030) - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0892: - case 0x10ec0897: - alc_update_coef_idx(codec, 0x7, 1<<5, 0); - break; - case 0x10ec0899: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1168: - case 0x10ec1220: - alc_update_coef_idx(codec, 0x7, 1<<1, 0); - break; - } -} + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; + int coef78; -/* additional initialization for ALC888 variants */ -static void alc888_coef_init(struct hda_codec *codec) -{ - switch (alc_get_coef0(codec) & 0x00f0) { - /* alc888-VA */ - case 0x00: - /* alc888-VB */ - case 0x10: - alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */ - break; - } -} + alc282_restore_default_value(codec); -/* turn on/off EAPD control (only if available) */ -static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) -{ - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + if (!hp_pin) return; - if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, - on ? 2 : 0); -} + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + coef78 = alc_read_coef_idx(codec, 0x78); -/* turn on/off EAPD controls of the codec */ -static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) -{ - /* We currently only handle front, HP */ - static const hda_nid_t pins[] = { - 0x0f, 0x10, 0x14, 0x15, 0x17, 0 - }; - const hda_nid_t *p; - for (p = pins; *p; p++) - set_eapd(codec, *p, on); -} + /* Index 0x78 Direct Drive HP AMP LPM Control 1 */ + /* Headphone capless set to high power mode */ + alc_write_coef_idx(codec, 0x78, 0x9004); -static int find_ext_mic_pin(struct hda_codec *codec); + if (hp_pin_sense) + msleep(2); -static void alc_headset_mic_no_shutup(struct hda_codec *codec) -{ - const struct hda_pincfg *pin; - int mic_pin = find_ext_mic_pin(codec); - int i; + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - /* don't shut up pins when unloading the driver; otherwise it breaks - * the default pin setup at the next load of the driver - */ - if (codec->bus->shutdown) - return; + if (hp_pin_sense) + msleep(85); - snd_array_for_each(&codec->init_pins, i, pin) { - /* use read here for syncing after issuing each verb */ - if (pin->nid != mic_pin) - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + if (hp_pin_sense) + msleep(100); - codec->pins_shutup = 1; + /* Headphone capless set to normal mode */ + alc_write_coef_idx(codec, 0x78, coef78); } -static void alc_shutup_pins(struct hda_codec *codec) +static void alc282_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; + int coef78; - if (spec->no_shutup_pins) + if (!hp_pin) { + alc269_shutup(codec); return; - - switch (codec->core.vendor_id) { - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - case 0x10ec0283: - case 0x10ec0285: - case 0x10ec0286: - case 0x10ec0287: - case 0x10ec0288: - case 0x10ec0295: - case 0x10ec0298: - alc_headset_mic_no_shutup(codec); - break; - default: - snd_hda_shutup_pins(codec); - break; } -} -/* generic shutup callback; - * just turning off EAPD and a little pause for avoiding pop-noise - */ -static void alc_eapd_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + coef78 = alc_read_coef_idx(codec, 0x78); + alc_write_coef_idx(codec, 0x78, 0x9004); - alc_auto_setup_eapd(codec, false); - if (!spec->no_depop_delay) - msleep(200); - alc_shutup_pins(codec); -} + if (hp_pin_sense) + msleep(2); -/* generic EAPD initialization */ -static void alc_auto_init_amp(struct hda_codec *codec, int type) -{ - alc_auto_setup_eapd(codec, true); - alc_write_gpio(codec); - switch (type) { - case ALC_INIT_DEFAULT: - switch (codec->core.vendor_id) { - case 0x10ec0260: - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); - break; - case 0x10ec0880: - case 0x10ec0882: - case 0x10ec0883: - case 0x10ec0885: - alc_update_coef_idx(codec, 7, 0, 0x2030); - break; - case 0x10ec0888: - alc888_coef_init(codec); - break; - } - break; - } -} + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); -/* get a primary headphone pin if available */ -static hda_nid_t alc_get_hp_pin(struct alc_spec *spec) -{ - if (spec->gen.autocfg.hp_pins[0]) - return spec->gen.autocfg.hp_pins[0]; - if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) - return spec->gen.autocfg.line_out_pins[0]; - return 0; -} + if (hp_pin_sense) + msleep(85); -/* - * Realtek SSID verification - */ + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) + if (hp_pin_sense) + msleep(100); -static void alc_fixup_sku_ignore(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; - } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); + alc_write_coef_idx(codec, 0x78, coef78); } -static void alc_fixup_no_depop_delay(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; +static const struct coef_fw alc283_coefs[] = { + WRITE_COEF(0x03, 0x0002), /* Power Down Control */ + UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ + WRITE_COEF(0x07, 0x0200), /* DMIC control */ + UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ + UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ + WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ + WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ + WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */ + UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ + UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ + WRITE_COEF(0x3a, 0x0), /* Class D test 4 */ + UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */ + WRITE_COEF(0x22, 0xa0c0), /* ANC */ + UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */ + UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ + UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ + WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ + WRITE_COEF(0x2e, 0x2902), /* PLL */ + WRITE_COEF(0x33, 0xa080), /* capless control 2 */ + WRITE_COEF(0x34, 0x3400), /* capless control 3 */ + WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */ + WRITE_COEF(0x36, 0x0), /* capless control 5 */ + UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */ + WRITE_COEF(0x39, 0x110a), /* class D test 3 */ + UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */ + WRITE_COEF(0x3c, 0x0014), /* class D test 6 */ + WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */ + UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */ + WRITE_COEF(0x49, 0x0), /* test mode */ + UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */ + UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */ + WRITE_COEF(0x37, 0xfc06), /* Class D amp control */ + UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */ + {} +}; - if (action == HDA_FIXUP_ACT_PROBE) { - spec->no_depop_delay = 1; - codec->depop_delay = 0; - } +static void alc283_restore_default_value(struct hda_codec *codec) +{ + alc_process_coef_fw(codec, alc283_coefs); } -static int alc_auto_parse_customize_define(struct hda_codec *codec) +static void alc283_init(struct hda_codec *codec) { - unsigned int ass, tmp, i; - unsigned nid = 0; struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + alc283_restore_default_value(codec); - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } + if (!hp_pin) + return; - if (!codec->bus->pci) - return -1; - ass = codec->core.subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->core.chip_name, ass); - return -1; - } + msleep(30); + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; - - spec->cdefine.port_connectivity = ass >> 30; - spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; - spec->cdefine.check_sum = (ass >> 16) & 0xf; - spec->cdefine.customization = ass >> 8; -do_sku: - spec->cdefine.sku_cfg = ass; - spec->cdefine.external_amp = (ass & 0x38) >> 3; - spec->cdefine.platform_type = (ass & 0x4) >> 2; - spec->cdefine.swap = (ass & 0x2) >> 1; - spec->cdefine.override = ass & 0x1; - - codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n", - nid, spec->cdefine.sku_cfg); - codec_dbg(codec, "SKU: port_connectivity=0x%x\n", - spec->cdefine.port_connectivity); - codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); - codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); - codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization); - codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp); - codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type); - codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap); - codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override); + /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ + /* Headphone capless set to high power mode */ + alc_write_coef_idx(codec, 0x43, 0x9004); - return 0; -} + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} -/* return true if the given NID is found in the list */ -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - return find_idx_in_nid_list(nid, list, nums) >= 0; + if (hp_pin_sense) + msleep(85); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + if (hp_pin_sense) + msleep(85); + /* Index 0x46 Combo jack auto switch control 2 */ + /* 3k pull low control for Headset jack. */ + alc_update_coef_idx(codec, 0x46, 3 << 12, 0); + /* Headphone capless set to normal mode */ + alc_write_coef_idx(codec, 0x43, 0x9614); } -/* check subsystem ID and set up device-specific initialization; - * return 1 if initialized, 0 if invalid SSID - */ -/* 32-bit subsystem ID for BIOS loading in HD Audio codec. - * 31 ~ 16 : Manufacture ID - * 15 ~ 8 : SKU ID - * 7 ~ 0 : Assembly ID - * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 - */ -static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) +static void alc283_shutup(struct hda_codec *codec) { - unsigned int ass, tmp, i; - unsigned nid; struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return 0; - goto do_sku; - } - - ass = codec->core.subsystem_id & 0xffff; - if (codec->bus->pci && - ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - /* invalid SSID, check the special NID pin defcfg instead */ - /* - * 31~30 : port connectivity - * 29~21 : reserve - * 20 : PCBEEP input - * 19~16 : Check sum (15:1) - * 15~1 : Custom - * 0 : override - */ - nid = 0x1d; - if (codec->core.vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - codec_dbg(codec, - "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n", - ass, nid); - if (!(ass & 1)) - return 0; - if ((ass >> 30) != 1) /* no physical connection */ - return 0; - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return 0; -do_sku: - codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->core.vendor_id); - /* - * 0 : override - * 1 : Swap Jack - * 2 : 0 --> Desktop, 1 --> Laptop - * 3~5 : External Amplifier control - * 7~6 : Reserved - */ - tmp = (ass & 0x38) >> 3; /* external Amp control */ - if (spec->init_amp == ALC_INIT_UNDEFINED) { - switch (tmp) { - case 1: - alc_setup_gpio(codec, 0x01); - break; - case 3: - alc_setup_gpio(codec, 0x02); - break; - case 7: - alc_setup_gpio(codec, 0x04); - break; - case 5: - default: - spec->init_amp = ALC_INIT_DEFAULT; - break; - } + if (!hp_pin) { + alc269_shutup(codec); + return; } - /* is laptop or Desktop and enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!(ass & 0x8000)) - return 1; - /* - * 10~8 : Jack location - * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered - * 14~13: Resvered - * 15 : 1 --> enable the function "Mute internal speaker - * when the external headphone out jack is plugged" - */ - if (!alc_get_hp_pin(spec)) { - hda_nid_t nid; - tmp = (ass >> 11) & 0x3; /* HP to chassis */ - nid = ports[tmp]; - if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, - spec->gen.autocfg.line_outs)) - return 1; - spec->gen.autocfg.hp_pins[0] = nid; - } - return 1; -} + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); -/* Check the validity of ALC subsystem-id - * ports contains an array of 4 pin NIDs for port-A, E, D and I */ -static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) -{ - if (!alc_subsystem_id(codec, ports)) { - struct alc_spec *spec = codec->spec; - if (spec->init_amp == ALC_INIT_UNDEFINED) { - codec_dbg(codec, - "realtek: Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; - } - } -} + alc_write_coef_idx(codec, 0x43, 0x9004); -/* inverted digital-mic */ -static void alc_fixup_inv_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; + /*depop hp during suspend*/ + alc_write_coef_idx(codec, 0x06, 0x2100); - spec->gen.inv_dmic_split = 1; -} + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp_pin_sense) + msleep(100); -static int alc_build_controls(struct hda_codec *codec) -{ - int err; + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); - return 0; + if (hp_pin_sense) + msleep(100); + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); + alc_write_coef_idx(codec, 0x43, 0x9614); } +static void alc256_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; -/* - * Common callbacks - */ + if (spec->ultra_low_power) { + alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1); + alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2); + alc_update_coef_idx(codec, 0x08, 7<<4, 0); + alc_update_coef_idx(codec, 0x3b, 1<<15, 0); + alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); + msleep(30); + } -static void alc_pre_init(struct hda_codec *codec) -{ - alc_fill_eapd_coef(codec); -} + if (!hp_pin) + hp_pin = 0x21; -#define is_s3_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) -#define is_s4_resume(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) -#define is_s4_suspend(codec) \ - ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) + msleep(30); -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - /* hibernation resume needs the full chip initialization */ - if (is_s4_resume(codec)) - alc_pre_init(codec); + if (hp_pin_sense) { + msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - if (spec->init_hook) - spec->init_hook(codec); + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - spec->gen.skip_verbs = 1; /* applied in below */ - snd_hda_gen_init(codec); - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - snd_hda_apply_verbs(codec); /* apply verbs here after own init */ + msleep(75); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - return 0; + msleep(75); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + } + alc_update_coef_idx(codec, 0x46, 3 << 12, 0); + alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ + alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); + /* + * Expose headphone mic (or possibly Line In on some machines) instead + * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See + * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of + * this register. + */ + alc_write_coef_idx(codec, 0x36, 0x5757); } -/* forward declaration */ -static const struct component_master_ops comp_master_ops; - -static void alc_free(struct hda_codec *codec) +static void alc256_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; - if (spec) - hda_component_manager_free(&spec->comps, &comp_master_ops); + if (!hp_pin) + hp_pin = 0x21; - snd_hda_gen_free(codec); -} + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; + if (hp_pin_sense) { + msleep(2); - if (!snd_hda_get_bool_hint(codec, "shutup")) - return; /* disabled explicitly by hints */ + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (spec && spec->shutup) - spec->shutup(codec); - else - alc_shutup_pins(codec); -} + msleep(75); -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} + /* 3k pull low control for Headset jack. */ + /* NOTE: call this before clearing the pin, otherwise codec stalls */ + /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly + * when booting with headset plugged. So skip setting it for the codec alc257 + */ + if (spec->en_3kpull_low) + alc_update_coef_idx(codec, 0x46, 0, 3 << 12); -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + } + + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); + if (spec->ultra_low_power) { + msleep(50); + alc_update_coef_idx(codec, 0x03, 1<<1, 0); + alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4); + alc_update_coef_idx(codec, 0x08, 3<<2, 0); + alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15); + alc_update_coef_idx(codec, 0x0e, 7<<6, 0); + msleep(30); + } } -static int alc_resume(struct hda_codec *codec) +static void alc285_hp_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + int i, val; + int coef38, coef0d, coef36; - if (!spec->no_depop_delay) - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - return 0; -} + alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */ + alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */ + coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */ + coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */ + coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */ + alc_update_coef_idx(codec, 0x38, 1<<4, 0x0); + alc_update_coef_idx(codec, 0x0d, 0x110, 0x0); -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, - .resume = alc_resume, - .suspend = alc_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + if (hp_pin) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); -#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) + msleep(130); + alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14); + alc_update_coef_idx(codec, 0x36, 1<<13, 0x0); -/* - * Rename codecs appropriately from COEF value or subvendor id - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; + if (hp_pin) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(10); + alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */ + alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880); + alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049); + alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0); -struct alc_codec_rename_pci_table { - unsigned int codec_vendor_id; - unsigned short pci_subvendor; - unsigned short pci_subdevice; - const char *name; -}; + alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */ + val = alc_read_coefex_idx(codec, 0x58, 0x00); + for (i = 0; i < 20 && val & 0x8000; i++) { + msleep(50); + val = alc_read_coefex_idx(codec, 0x58, 0x00); + } /* Wait for depop procedure finish */ -static const struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; + alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ + alc_update_coef_idx(codec, 0x38, 1<<4, coef38); + alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); + alc_update_coef_idx(codec, 0x36, 3<<13, coef36); -static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { - { 0x10ec0280, 0x1028, 0, "ALC3220" }, - { 0x10ec0282, 0x1028, 0, "ALC3221" }, - { 0x10ec0283, 0x1028, 0, "ALC3223" }, - { 0x10ec0288, 0x1028, 0, "ALC3263" }, - { 0x10ec0292, 0x1028, 0, "ALC3226" }, - { 0x10ec0293, 0x1028, 0, "ALC3235" }, - { 0x10ec0255, 0x1028, 0, "ALC3234" }, - { 0x10ec0668, 0x1028, 0, "ALC3661" }, - { 0x10ec0275, 0x1028, 0, "ALC3260" }, - { 0x10ec0899, 0x1028, 0, "ALC3861" }, - { 0x10ec0298, 0x1028, 0, "ALC3266" }, - { 0x10ec0236, 0x1028, 0, "ALC3204" }, - { 0x10ec0256, 0x1028, 0, "ALC3246" }, - { 0x10ec0225, 0x1028, 0, "ALC3253" }, - { 0x10ec0295, 0x1028, 0, "ALC3254" }, - { 0x10ec0299, 0x1028, 0, "ALC3271" }, - { 0x10ec0670, 0x1025, 0, "ALC669X" }, - { 0x10ec0676, 0x1025, 0, "ALC679X" }, - { 0x10ec0282, 0x1043, 0, "ALC3229" }, - { 0x10ec0233, 0x1043, 0, "ALC3236" }, - { 0x10ec0280, 0x103c, 0, "ALC3228" }, - { 0x10ec0282, 0x103c, 0, "ALC3227" }, - { 0x10ec0286, 0x103c, 0, "ALC3242" }, - { 0x10ec0290, 0x103c, 0, "ALC3241" }, - { 0x10ec0668, 0x103c, 0, "ALC3662" }, - { 0x10ec0283, 0x17aa, 0, "ALC3239" }, - { 0x10ec0292, 0x17aa, 0, "ALC3232" }, - { 0x10ec0257, 0x12f0, 0, "ALC3328" }, - { } /* terminator */ -}; + msleep(50); + alc_update_coef_idx(codec, 0x4a, 1<<15, 0); +} -static int alc_codec_rename_from_preset(struct hda_codec *codec) +static void alc225_init(struct hda_codec *codec) { - const struct alc_codec_rename_table *p; - const struct alc_codec_rename_pci_table *q; + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->core.vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); + if (spec->ultra_low_power) { + alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2); + alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); + alc_update_coef_idx(codec, 0x33, 1<<11, 0); + msleep(30); } - if (!codec->bus->pci) - return 0; - for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->core.vendor_id) - continue; - if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) - continue; - if (!q->pci_subdevice || - q->pci_subdevice == codec->bus->pci->subsystem_device) - return alc_codec_rename(codec, q->name); - } + if (spec->codec_variant != ALC269_TYPE_ALC287 && + spec->codec_variant != ALC269_TYPE_ALC245) + /* required only at boot or S3 and S4 resume time */ + if (!spec->done_hp_init || + is_s3_resume(codec) || + is_s4_resume(codec)) { + alc285_hp_init(codec); + spec->done_hp_init = true; + } - return 0; -} + if (!hp_pin) + hp_pin = 0x21; + msleep(30); + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); -/* - * Digital-beep handlers - */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ -/* additional beep mixers; private_value will be overwritten */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), -}; + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); -/* set up and create beep controls */ -static int set_beep_amp(struct alc_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - struct snd_kcontrol_new *knew; - unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); - int i; + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { - knew = snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc_beep_mixer[i]); - if (!knew) - return -ENOMEM; - knew->private_value = beep_amp; + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ } - return 0; } -static const struct snd_pci_quirk beep_allow_list[] = { - SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), - SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), - SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), - SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), - SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), - /* denylist -- no beep available */ - SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0), - SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0), - {} -}; +static void alc225_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + hp_pin = 0x21; + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); + + if (hp1_pin_sense || hp2_pin_sense) { + alc_disable_headset_jack_key(codec); + /* 3k pull low control for Headset jack. */ + alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); + alc_enable_headset_jack_key(codec); + } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); + if (spec->ultra_low_power) { + msleep(50); + alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2); + alc_update_coef_idx(codec, 0x0e, 7<<6, 0); + alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11); + alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); + msleep(30); + } +} -static inline int has_cdefine_beep(struct hda_codec *codec) +static void alc222_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list); - if (q) - return q->value; - return spec->cdefine.enable_pcbeep; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + return; + + msleep(30); + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); + + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + msleep(75); + } } -#else -#define set_beep_amp(spec, nid, idx, dir) 0 -#define has_cdefine_beep(codec) 0 -#endif -/* parse the BIOS configuration and set up the alc_spec */ -/* return 1 if successful, 0 if the proper config is not found, - * or a negative error code - */ -static int alc_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids, - const hda_nid_t *ssid_nids) +static void alc222_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; - err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; + if (!hp_pin) + hp_pin = 0x21; - if (ssid_nids) - alc_ssid_check(codec, ssid_nids); + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - return 1; + msleep(75); + } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); } -/* common preparation job for alc_spec */ -static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) +static void alc_default_init(struct hda_codec *codec) { - struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - int err; + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; - if (!spec) - return -ENOMEM; - codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); - spec->gen.mixer_nid = mixer_nid; - spec->gen.own_eapd_ctl = 1; - codec->single_adc_amp = 1; - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - codec->forced_resume = 1; - codec->patch_ops = alc_patch_ops; - mutex_init(&spec->coef_mutex); - - err = alc_codec_rename_from_preset(codec); - if (err < 0) { - kfree(spec); - return err; + if (!hp_pin) + return; + + msleep(30); + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) { + msleep(2); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + msleep(75); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(75); } - return 0; } -static int alc880_parse_auto_config(struct hda_codec *codec) +static void alc_default_shutup(struct hda_codec *codec) { - static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp_pin_sense; + + if (!hp_pin) { + alc269_shutup(codec); + return; + } + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + if (hp_pin_sense) { + msleep(2); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(75); + + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); } -/* - * ALC880 fix-ups - */ -enum { - ALC880_FIXUP_GPIO1, - ALC880_FIXUP_GPIO2, - ALC880_FIXUP_MEDION_RIM, - ALC880_FIXUP_LG, - ALC880_FIXUP_LG_LW25, - ALC880_FIXUP_W810, - ALC880_FIXUP_EAPD_COEF, - ALC880_FIXUP_TCL_S700, - ALC880_FIXUP_VOL_KNOB, - ALC880_FIXUP_FUJITSU, - ALC880_FIXUP_F1734, - ALC880_FIXUP_UNIWILL, - ALC880_FIXUP_UNIWILL_DIG, - ALC880_FIXUP_Z71V, - ALC880_FIXUP_ASUS_W5A, - ALC880_FIXUP_3ST_BASE, - ALC880_FIXUP_3ST, - ALC880_FIXUP_3ST_DIG, - ALC880_FIXUP_5ST_BASE, - ALC880_FIXUP_5ST, - ALC880_FIXUP_5ST_DIG, - ALC880_FIXUP_6ST_BASE, - ALC880_FIXUP_6ST, - ALC880_FIXUP_6ST_DIG, - ALC880_FIXUP_6ST_AUTOMUTE, -}; +static void alc294_hp_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + int i, val; -/* enable the volume-knob widget support on NID 0x21 */ -static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) + if (!hp_pin) + return; + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(100); + + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ + alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ + + /* Wait for depop procedure finish */ + val = alc_read_coefex_idx(codec, 0x58, 0x01); + for (i = 0; i < 20 && val & 0x0080; i++) { + msleep(50); + val = alc_read_coefex_idx(codec, 0x58, 0x01); + } + /* Set HP depop to auto mode */ + alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); + msleep(50); +} + +static void alc294_init(struct hda_codec *codec) { - if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc_update_knob_master); + struct alc_spec *spec = codec->spec; + + /* required only at boot or S4 resume time */ + if (!spec->done_hp_init || + codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) { + alc294_hp_init(codec); + spec->done_hp_init = true; + } + alc_default_init(codec); } -static const struct hda_fixup alc880_fixups[] = { - [ALC880_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC880_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC880_FIXUP_MEDION_RIM] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_LG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x16, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_LG_LW25] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x0181344f }, /* line-in */ - { 0x1b, 0x0321403f }, /* headphone */ - { } - } - }, - [ALC880_FIXUP_W810] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_EAPD_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - {} - }, - }, - [ALC880_FIXUP_TCL_S700] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO2, - }, - [ALC880_FIXUP_VOL_KNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc880_fixup_vol_knob, - }, - [ALC880_FIXUP_FUJITSU] = { - /* override all pins as BIOS on old Amilo is broken */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x01454140 }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_F1734] = { - /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121401f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x411111f0 }, /* N/A */ - { 0x19, 0x01a19950 }, /* mic-in */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_VOL_KNOB, - }, - [ALC880_FIXUP_UNIWILL] = { - /* need to fix HP and speaker pins to be parsed correctly */ - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x99030120 }, /* speaker */ - { 0x16, 0x99030130 }, /* bass speaker */ - { } - }, - }, - [ALC880_FIXUP_UNIWILL_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* disable bogus unused pins */ - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1f, 0x411111f0 }, - { } - } - }, - [ALC880_FIXUP_Z71V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x99030120 }, /* speaker */ - { 0x15, 0x0121411f }, /* HP */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19950 }, /* mic-in */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - } - }, - [ALC880_FIXUP_ASUS_W5A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* set up the whole pins as BIOS is utterly broken */ - { 0x14, 0x0121411f }, /* HP */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x90a60160 }, /* mic */ - { 0x19, 0x411111f0 }, /* N/A */ - { 0x1a, 0x411111f0 }, /* N/A */ - { 0x1b, 0x411111f0 }, /* N/A */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - { 0x1e, 0xb743111e }, /* SPDIF out */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_GPIO1, - }, - [ALC880_FIXUP_3ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* line-out */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x411111f0 }, /* N/A */ - { 0x17, 0x411111f0 }, /* N/A */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_3ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_3ST_BASE, - }, - [ALC880_FIXUP_5ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x411111f0 }, /* N/A */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01016412 }, /* surr */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x0121411f }, /* HP */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x02a19c40 }, /* front-mic */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_5ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_5ST_BASE, - }, - [ALC880_FIXUP_6ST_BASE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x01014010 }, /* front */ - { 0x15, 0x01016412 }, /* surr */ - { 0x16, 0x01011411 }, /* CLFE */ - { 0x17, 0x01012414 }, /* side */ - { 0x18, 0x01a19c30 }, /* mic-in */ - { 0x19, 0x02a19c40 }, /* front-mic */ - { 0x1a, 0x01813031 }, /* line-in */ - { 0x1b, 0x0121411f }, /* HP */ - { 0x1c, 0x411111f0 }, /* N/A */ - { 0x1d, 0x411111f0 }, /* N/A */ - /* 0x1e is filled in below */ - { 0x1f, 0x411111f0 }, /* N/A */ - { } - } - }, - [ALC880_FIXUP_6ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x411111f0 }, /* N/A */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_DIG] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x0144111e }, /* SPDIF */ - { } - }, - .chained = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, - [ALC880_FIXUP_6ST_AUTOMUTE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0121401f }, /* HP with jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC880_FIXUP_6ST_BASE, - }, -}; - -static const struct hda_quirk alc880_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), - SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), - SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), - SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), - SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), - SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), - SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), - SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), - SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), - SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), - - /* Below is the copied entries from alc880_quirks.c. - * It's not quite sure whether BIOS sets the correct pin-config table - * on these machines, thus they are kept to be compatible with - * the old static quirks. Once when it's confirmed to work without - * these overrides, it'd be better to remove. - */ - SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), - SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), - SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), - /* default Intel */ - SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), - {} -}; - -static const struct hda_model_fixup alc880_fixup_models[] = { - {.id = ALC880_FIXUP_3ST, .name = "3stack"}, - {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, - {.id = ALC880_FIXUP_5ST, .name = "5stack"}, - {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, - {.id = ALC880_FIXUP_6ST, .name = "6stack"}, - {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, - {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, - {} -}; - - -/* - * OK, here we have finally the patch for ALC880 - */ -static int patch_alc880(struct hda_codec *codec) +static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, + unsigned int val) { - struct alc_spec *spec; - int err; + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */ + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */ +} - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; +static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg) +{ + unsigned int val; - spec = codec->spec; - spec->gen.need_dac_fix = 1; - spec->gen.beep_nid = 0x01; + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); + val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) + & 0xffff; + val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) + << 16; + return val; +} - codec->patch_ops.unsol_event = alc880_unsol_event; +static void alc5505_dsp_halt(struct hda_codec *codec) +{ + unsigned int val; - alc_pre_init(codec); + alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */ + alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */ + alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */ + alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */ + alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */ + alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */ + alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */ + val = alc5505_coef_get(codec, 0x6220); + alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */ +} - snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, - alc880_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); +static void alc5505_dsp_back_from_halt(struct hda_codec *codec) +{ + alc5505_coef_set(codec, 0x61b8, 0x04133302); + alc5505_coef_set(codec, 0x61b0, 0x00005b16); + alc5505_coef_set(codec, 0x61b4, 0x040a2b02); + alc5505_coef_set(codec, 0x6230, 0xf80d4011); + alc5505_coef_set(codec, 0x6220, 0x2002010f); + alc5505_coef_set(codec, 0x880c, 0x00000004); +} - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; +static void alc5505_dsp_init(struct hda_codec *codec) +{ + unsigned int val; - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } + alc5505_dsp_halt(codec); + alc5505_dsp_back_from_halt(codec); + alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */ + alc5505_coef_set(codec, 0x61b0, 0x5b16); + alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */ + alc5505_coef_set(codec, 0x61b4, 0x04132b02); + alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/ + alc5505_coef_set(codec, 0x61b8, 0x041f3302); + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */ + alc5505_coef_set(codec, 0x61b8, 0x041b3302); + alc5505_coef_set(codec, 0x61b8, 0x04173302); + alc5505_coef_set(codec, 0x61b8, 0x04163302); + alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */ + alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */ + alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */ - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */ + if (val <= 3) + alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */ + else + alc5505_coef_set(codec, 0x6220, 0x6002018f); - return 0; + alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/ + alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */ + alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */ + alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */ + alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ + alc5505_coef_set(codec, 0x880c, 0x00000003); + alc5505_coef_set(codec, 0x880c, 0x00000010); - error: - alc_free(codec); - return err; +#ifdef HALT_REALTEK_ALC5505 + alc5505_dsp_halt(codec); +#endif } +#ifdef HALT_REALTEK_ALC5505 +#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */ +#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */ +#else +#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec) +#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec) +#endif -/* - * ALC260 support - */ -static int alc260_parse_auto_config(struct hda_codec *codec) +static int alc269_suspend(struct hda_codec *codec) { - static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; - static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; - return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); -} + struct alc_spec *spec = codec->spec; -/* - * Pin config fixes - */ -enum { - ALC260_FIXUP_HP_DC5750, - ALC260_FIXUP_HP_PIN_0F, - ALC260_FIXUP_COEF, - ALC260_FIXUP_GPIO1, - ALC260_FIXUP_GPIO1_TOGGLE, - ALC260_FIXUP_REPLACER, - ALC260_FIXUP_HP_B1900, - ALC260_FIXUP_KN1, - ALC260_FIXUP_FSC_S7020, - ALC260_FIXUP_FSC_S7020_JWSE, - ALC260_FIXUP_VAIO_PINS, -}; + if (spec->has_alc5505_dsp) + alc5505_dsp_suspend(codec); -static void alc260_gpio1_automute(struct hda_codec *codec) + return alc_suspend(codec); +} + +static int alc269_resume(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); -} - -static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { - /* although the machine has only one output pin, we need to - * toggle GPIO1 according to the jack state - */ - spec->gen.automute_hook = alc260_gpio1_automute; - spec->gen.detect_hp = 1; - spec->gen.automute_speaker = 1; - spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, - snd_hda_gen_hp_automute); - alc_setup_gpio(codec, 0x01); - } -} - -static void alc260_fixup_kn1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x0f, 0x02214000 }, /* HP/speaker */ - { 0x12, 0x90a60160 }, /* int mic */ - { 0x13, 0x02a19000 }, /* ext mic */ - { 0x18, 0x01446000 }, /* SPDIF out */ - /* disable bogus I/O pins */ - { 0x10, 0x411111f0 }, - { 0x11, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - spec->init_amp = ALC_INIT_NONE; - break; + if (spec->codec_variant == ALC269_TYPE_ALC269VB) + alc269vb_toggle_power_output(codec, 0); + if (spec->codec_variant == ALC269_TYPE_ALC269VB && + (alc_get_coef0(codec) & 0x00ff) == 0x018) { + msleep(150); } -} -static void alc260_fixup_fsc_s7020(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->init_amp = ALC_INIT_NONE; -} + codec->patch_ops.init(codec); -static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.add_jack_modes = 1; - spec->gen.hp_mic = 1; + if (spec->codec_variant == ALC269_TYPE_ALC269VB) + alc269vb_toggle_power_output(codec, 1); + if (spec->codec_variant == ALC269_TYPE_ALC269VB && + (alc_get_coef0(codec) & 0x00ff) == 0x017) { + msleep(200); } -} - -static const struct hda_fixup alc260_fixups[] = { - [ALC260_FIXUP_HP_DC5750] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x11, 0x90130110 }, /* speaker */ - { } - } - }, - [ALC260_FIXUP_HP_PIN_0F] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x0f, 0x01214000 }, /* HP */ - { } - } - }, - [ALC260_FIXUP_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, - { } - }, - }, - [ALC260_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_HP_PIN_0F, - }, - [ALC260_FIXUP_REPLACER] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, - }, - [ALC260_FIXUP_HP_B1900] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_gpio1_toggle, - .chained = true, - .chain_id = ALC260_FIXUP_COEF, - }, - [ALC260_FIXUP_KN1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_kn1, - }, - [ALC260_FIXUP_FSC_S7020] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020, - }, - [ALC260_FIXUP_FSC_S7020_JWSE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc260_fixup_fsc_s7020_jwse, - .chained = true, - .chain_id = ALC260_FIXUP_FSC_S7020, - }, - [ALC260_FIXUP_VAIO_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* Pin configs are missing completely on some VAIOs */ - { 0x0f, 0x01211020 }, - { 0x10, 0x0001003f }, - { 0x11, 0x411111f0 }, - { 0x12, 0x01a15930 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x411111f0 }, - { 0x15, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x411111f0 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x411111f0 }, - { } - } - }, -}; - -static const struct hda_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), - SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), - SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), - SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), - SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), - SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), - SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), - SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), - SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc260_fixup_models[] = { - {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC260_FIXUP_COEF, .name = "coef"}, - {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, - {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, - {} -}; - -/* - */ -static int patch_alc260(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - err = alc_alloc_spec(codec, 0x07); - if (err < 0) - return err; + snd_hda_regmap_sync(codec); + hda_call_check_power_status(codec, 0x01); - spec = codec->spec; - /* as quite a few machines require HP amp for speaker outputs, - * it's easier to enable it unconditionally; even if it's unneeded, - * it's almost harmless. + /* on some machine, the BIOS will clear the codec gpio data when enter + * suspend, and won't restore the data after resume, so we restore it + * in the driver. */ - spec->gen.prefer_hp_amp = 1; - spec->gen.beep_nid = 0x01; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, - alc260_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } + if (spec->gpio_data) + alc_write_gpio_data(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + if (spec->has_alc5505_dsp) + alc5505_dsp_resume(codec); return 0; - - error: - alc_free(codec); - return err; } - -/* - * ALC882/883/885/888/889 support - * - * ALC882 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * Pin config fixes - */ -enum { - ALC882_FIXUP_ABIT_AW9D_MAX, - ALC882_FIXUP_LENOVO_Y530, - ALC882_FIXUP_PB_M5210, - ALC882_FIXUP_ACER_ASPIRE_7736, - ALC882_FIXUP_ASUS_W90V, - ALC889_FIXUP_CD, - ALC889_FIXUP_FRONT_HP_NO_PRESENCE, - ALC889_FIXUP_VAIO_TT, - ALC888_FIXUP_EEE1601, - ALC886_FIXUP_EAPD, - ALC882_FIXUP_EAPD, - ALC883_FIXUP_EAPD, - ALC883_FIXUP_ACER_EAPD, - ALC882_FIXUP_GPIO1, - ALC882_FIXUP_GPIO2, - ALC882_FIXUP_GPIO3, - ALC889_FIXUP_COEF, - ALC882_FIXUP_ASUS_W2JC, - ALC882_FIXUP_ACER_ASPIRE_4930G, - ALC882_FIXUP_ACER_ASPIRE_8930G, - ALC882_FIXUP_ASPIRE_8930G_VERBS, - ALC885_FIXUP_MACPRO_GPIO, - ALC889_FIXUP_DAC_ROUTE, - ALC889_FIXUP_MBP_VREF, - ALC889_FIXUP_IMAC91_VREF, - ALC889_FIXUP_MBA11_VREF, - ALC889_FIXUP_MBA21_VREF, - ALC889_FIXUP_MP11_VREF, - ALC889_FIXUP_MP41_VREF, - ALC882_FIXUP_INV_DMIC, - ALC882_FIXUP_NO_PRIMARY_HP, - ALC887_FIXUP_ASUS_BASS, - ALC887_FIXUP_BASS_CHMAP, - ALC1220_FIXUP_GB_DUAL_CODECS, - ALC1220_FIXUP_GB_X570, - ALC1220_FIXUP_CLEVO_P950, - ALC1220_FIXUP_CLEVO_PB51ED, - ALC1220_FIXUP_CLEVO_PB51ED_PINS, - ALC887_FIXUP_ASUS_AUDIO, - ALC887_FIXUP_ASUS_HMIC, - ALCS1200A_FIXUP_MIC_VREF, - ALC888VD_FIXUP_MIC_100VREF, -}; - -static void alc889_fixup_coef(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_INIT) - return; - alc_update_coef_idx(codec, 7, 0, 0x2030); + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; } -/* set up GPIO at initialization */ -static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct alc_spec *spec = codec->spec; + unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21); + unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19); - spec->gpio_write_delay = true; - alc_fixup_gpio3(codec, fix, action); + if (cfg_headphone && cfg_headset_mic == 0x411111f0) + snd_hda_codec_set_pincfg(codec, 0x19, + (cfg_headphone & ~AC_DEFCFG_DEVICE) | + (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT)); } -/* Fix the connection of some pins for ALC889: - * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't - * work correctly (bko#42740) - */ -static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hweq(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* fake the connections during parsing the tree */ - static const hda_nid_t conn1[] = { 0x0c, 0x0d }; - static const hda_nid_t conn2[] = { 0x0e, 0x0f }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2); - } else if (action == HDA_FIXUP_ACT_PROBE) { - /* restore the connections */ - static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn); - } + if (action == HDA_FIXUP_ACT_INIT) + alc_update_coef_idx(codec, 0x1e, 0, 0x80); } -/* Set VREF on HP pin */ -static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc271_fixup_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 }; - struct alc_spec *spec = codec->spec; - int i; + static const struct hda_verb verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, + {} + }; + unsigned int cfg; - if (action != HDA_FIXUP_ACT_INIT) + if (strcmp(codec->core.chip_name, "ALC271X") && + strcmp(codec->core.chip_name, "ALC269VB")) return; - for (i = 0; i < ARRAY_SIZE(nids); i++) { - unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); - if (get_defcfg_device(val) != AC_JACK_HP_OUT) - continue; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_80; - snd_hda_set_pin_ctl(codec, nids[i], val); - spec->gen.keep_vref_in_automute = 1; - break; - } + cfg = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) + snd_hda_sequence_write(codec, verbs); } -static void alc889_fixup_mac_pins(struct hda_codec *codec, - const hda_nid_t *nids, int num_nids) +/* Fix the speaker amp after resume, etc */ +static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < num_nids; i++) { - unsigned int val; - val = snd_hda_codec_get_pin_target(codec, nids[i]); - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, nids[i], val); - } - spec->gen.keep_vref_in_automute = 1; + if (action == HDA_FIXUP_ACT_INIT) + alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000); } -/* Set VREF on speaker pins on imac91 */ -static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_pcm_44k(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t nids[] = { 0x18, 0x1a }; + struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); + if (action != HDA_FIXUP_ACT_PROBE) + return; + + /* Due to a hardware problem on Lenovo Ideadpad, we need to + * fix the sample rate of analog I/O to 44.1kHz + */ + spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; } -/* Set VREF on speaker pins on mba11 */ -static void alc889_fixup_mba11_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t nids[] = { 0x18 }; - + /* The digital-mic unit sends PDM (differential signal) instead of + * the standard PCM, thus you can't record a valid mono stream as is. + * Below is a workaround specific to ALC269 to control the dmic + * signal source as mono. + */ if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); + alc_update_coef_idx(codec, 0x07, 0, 0x80); } -/* Set VREF on speaker pins on mba21 */ -static void alc889_fixup_mba21_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_quanta_automute(struct hda_codec *codec) { - static const hda_nid_t nids[] = { 0x18, 0x19 }; + snd_hda_gen_update_outputs(codec); - if (action == HDA_FIXUP_ACT_INIT) - alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); + alc_write_coef_idx(codec, 0x0c, 0x680); + alc_write_coef_idx(codec, 0x0c, 0x480); } -/* Don't take HP output as primary - * Strangely, the speaker output doesn't work on Vaio Z and some Vaio - * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 - */ -static void alc882_fixup_no_primary_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_quanta_mute(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.no_primary_hp = 1; - spec->gen.no_multi_io = 1; - } + if (action != HDA_FIXUP_ACT_PROBE) + return; + spec->gen.automute_hook = alc269_quanta_automute; } -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action); - -/* For dual-codec configuration, we need to disable some features to avoid - * conflicts of kctls and PCM streams - */ -static void alc_fixup_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_x101_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; + int vref; + msleep(200); + snd_hda_gen_hp_automute(codec, jack); - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - /* disable vmaster */ - spec->gen.suppress_vmaster = 1; - /* auto-mute and auto-mic switch don't work with multiple codecs */ - spec->gen.suppress_auto_mute = 1; - spec->gen.suppress_auto_mic = 1; - /* disable aamix as well */ - spec->gen.mixer_nid = 0; - /* add location prefix to avoid conflicts */ - codec->force_pin_prefix = 1; -} - -static void rename_ctl(struct hda_codec *codec, const char *oldname, - const char *newname) -{ - struct snd_kcontrol *kctl; - - kctl = snd_hda_find_mixer_ctl(codec, oldname); - if (kctl) - snd_ctl_rename(codec->card, kctl, newname); + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + msleep(100); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); + msleep(500); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); } -static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +/* + * Magic sequence to make Huawei Matebook X right speaker working (bko#197801) + */ +struct hda_alc298_mbxinit { + unsigned char value_0x23; + unsigned char value_0x25; +}; + +static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec, + const struct hda_alc298_mbxinit *initval, + bool first) { - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } + snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0); + alc_write_coef_idx(codec, 0x26, 0xb000); + + if (first) + snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0); + + snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); + alc_write_coef_idx(codec, 0x26, 0xf000); + alc_write_coef_idx(codec, 0x23, initval->value_0x23); + + if (initval->value_0x23 != 0x1e) + alc_write_coef_idx(codec, 0x25, initval->value_0x25); + + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); } -static void alc1220_fixup_gb_x570(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - static const hda_nid_t conn1[] = { 0x0c }; - static const struct coef_fw gb_x570_coefs[] = { - WRITE_COEF(0x07, 0x03c0), - WRITE_COEF(0x1a, 0x01c1), - WRITE_COEF(0x1b, 0x0202), - WRITE_COEF(0x43, 0x3005), + /* Initialization magic */ + static const struct hda_alc298_mbxinit dac_init[] = { + {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, + {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00}, + {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00}, + {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24}, + {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f}, + {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00}, + {0x2f, 0x00}, + {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, + {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c}, + {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80}, {} }; + const struct hda_alc298_mbxinit *seq; - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, gb_x570_coefs); - break; + if (action != HDA_FIXUP_ACT_INIT) + return; + + /* Start */ + snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00); + snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); + alc_write_coef_idx(codec, 0x26, 0xf000); + alc_write_coef_idx(codec, 0x22, 0x31); + alc_write_coef_idx(codec, 0x23, 0x0b); + alc_write_coef_idx(codec, 0x25, 0x00); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); + + for (seq = dac_init; seq->value_0x23; seq++) + alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init); +} + +static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook; } } -static void alc1220_fixup_clevo_p950(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin, + bool polarity, bool on) +{ + unsigned int pinval; + + if (!pin) + return; + if (polarity) + on = !on; + pinval = snd_hda_codec_get_pin_target(codec, pin); + pinval &= ~AC_PINCTL_VREFEN; + pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; + /* temporarily power up/down for setting VREF */ + snd_hda_power_up_pm(codec); + snd_hda_set_pin_ctl_cache(codec, pin, pinval); + snd_hda_power_down_pm(codec); +} + +/* update mute-LED according to the speaker mute state via mic VREF pin */ +static int vref_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_vref_led(codec, spec->mute_led_nid, + spec->mute_led_polarity, brightness); + return 0; +} + +/* Make sure the led works even in runtime suspend */ +static unsigned int led_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + struct alc_spec *spec = codec->spec; + + if (power_state != AC_PWRST_D3 || nid == 0 || + (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid)) + return power_state; + + /* Set pin ctl again, it might have just been set to 0 */ + snd_hda_set_pin_ctl(codec, nid, + snd_hda_codec_get_pin_target(codec, nid)); + + return snd_hda_gen_path_power_filter(codec, nid, power_state); +} + +static void alc269_fixup_hp_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t conn1[] = { 0x0c }; + struct alc_spec *spec = codec->spec; + const struct dmi_device *dev = NULL; if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - alc_update_coef_idx(codec, 0x7, 0, 0x3c3); - /* We therefore want to make sure 0x14 (front headphone) and - * 0x1b (speakers) use the stereo DAC 0x02 - */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + int pol, pin; + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) + continue; + if (pin < 0x0a || pin >= 0x10) + break; + spec->mute_led_polarity = pol; + spec->mute_led_nid = pin - 0x0a + 0x18; + snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); + codec->power_filter = led_power_filter; + codec_dbg(codec, + "Detected mute LED for %x:%d\n", spec->mute_led_nid, + spec->mute_led_polarity); + break; + } +} + +static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec, + const struct hda_fixup *fix, + int action, hda_nid_t pin) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_nid = pin; + snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); + codec->power_filter = led_power_filter; + } +} + +static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18); +} + +static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19); } -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action); +static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b); +} -static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void alc236_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - alc1220_fixup_clevo_p950(codec, fix, action); - alc_fixup_headset_mode_no_hp_mic(codec, fix, action); + alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01); } -static void alc887_asus_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) +static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); +} + +static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01); +} + +static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); +} + +static void alc287_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x10, 0); +} + +static void alc245_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - unsigned int vref; - snd_hda_gen_hp_automute(codec, jack); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->micmute_led_polarity = 1; + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); +} - if (spec->gen.hp_jack_present) - vref = AC_PINCTL_VREF_80; - else - vref = AC_PINCTL_VREF_HIZ; - snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); +/* turn on/off mic-mute LED per capture hook via VREF change */ +static int vref_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_vref_led(codec, spec->cap_mute_led_nid, + spec->micmute_led_polarity, brightness); + return 0; } -static void alc887_fixup_asus_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) - return; - snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); - spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; + + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* Like hp_gpio_mic1_led, but also needs GPIO4 low to + * enable headphone amp + */ + spec->gpio_mask |= 0x10; + spec->gpio_dir |= 0x10; + spec->cap_mute_led_nid = 0x18; + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); + codec->power_filter = led_power_filter; + } } -static const struct hda_fixup alc882_fixups[] = { - [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x01080104 }, /* side */ - { 0x16, 0x01011012 }, /* rear */ - { 0x17, 0x01016011 }, /* clfe */ - { } - } - }, - [ALC882_FIXUP_LENOVO_Y530] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* rear int speakers */ - { 0x16, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - } - }, - [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC882_FIXUP_ASUS_W90V] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130110 }, /* fix sequence for CLFE */ - { } - } - }, - [ALC889_FIXUP_CD] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1c, 0x993301f0 }, /* CD */ - { } - } - }, - [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */ - { } - }, - .chained = true, - .chain_id = ALC889_FIXUP_CD, - }, - [ALC889_FIXUP_VAIO_TT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170111 }, /* hidden surround speaker */ - { } - } - }, - [ALC888_FIXUP_EEE1601] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, - { } - } - }, - [ALC886_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 }, - { } - } - }, - [ALC882_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, - { } - } - }, - [ALC883_FIXUP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* change to EAPD mode */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - { } - } - }, - [ALC883_FIXUP_ACER_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* eanable EAPD on Acer laptops */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - } - }, - [ALC882_FIXUP_GPIO1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - }, - [ALC882_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC882_FIXUP_GPIO3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio3, - }, - [ALC882_FIXUP_ASUS_W2JC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio1, - .chained = true, - .chain_id = ALC882_FIXUP_EAPD, - }, - [ALC889_FIXUP_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_coef, - }, - [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x17, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130111 }, /* CLFE speaker */ - { 0x1b, 0x99130112 }, /* surround speaker */ - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, - }, - [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { - /* additional init verbs for Acer Aspire 8930G */ - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enable all DACs */ - /* DAC DISABLE/MUTE 1? */ - /* setting bits 1-5 disables DAC nids 0x02-0x06 - * apparently. Init=0x38 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DAC DISABLE/MUTE 2? */ - /* some bit here disables the other DACs. - * Init=0x4900 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, - /* DMIC fix - * This laptop has a stereo digital microphone. - * The mics are only 1cm apart which makes the stereo - * useless. However, either the mic or the ALC889 - * makes the signal become a difference/sum signal - * instead of standard stereo, which is annoying. - * So instead we flip this bit which makes the - * codec replicate the sum signal to both channels, - * turning it into a normal mono mic. - */ - /* DMIC_CONTROL? Init value = 0x0001 */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - { } - }, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC885_FIXUP_MACPRO_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc885_fixup_macpro_gpio, - }, - [ALC889_FIXUP_DAC_ROUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_dac_route, - }, - [ALC889_FIXUP_MBP_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_IMAC91_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_imac91_vref, - .chained = true, - .chain_id = ALC882_FIXUP_GPIO1, - }, - [ALC889_FIXUP_MBA11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MBA21_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba21_vref, - .chained = true, - .chain_id = ALC889_FIXUP_MBP_VREF, - }, - [ALC889_FIXUP_MP11_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mba11_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC889_FIXUP_MP41_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc889_fixup_mbp_vref, - .chained = true, - .chain_id = ALC885_FIXUP_MACPRO_GPIO, - }, - [ALC882_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc882_fixup_no_primary_hp, - }, - [ALC887_FIXUP_ASUS_BASS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x99130130}, /* bass speaker */ - {} - }, - .chained = true, - .chain_id = ALC887_FIXUP_BASS_CHMAP, - }, - [ALC887_FIXUP_BASS_CHMAP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, - }, - [ALC1220_FIXUP_GB_DUAL_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_dual_codecs, - }, - [ALC1220_FIXUP_GB_X570] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_gb_x570, - }, - [ALC1220_FIXUP_CLEVO_P950] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_p950, - }, - [ALC1220_FIXUP_CLEVO_PB51ED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc1220_fixup_clevo_pb51ed, - }, - [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - {} - }, - .chained = true, - .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, - }, - [ALC887_FIXUP_ASUS_AUDIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ - { 0x19, 0x22219420 }, - {} - }, - }, - [ALC887_FIXUP_ASUS_HMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc887_fixup_asus_jack, - .chained = true, - .chain_id = ALC887_FIXUP_ASUS_AUDIO, - }, - [ALCS1200A_FIXUP_MIC_VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF50 }, /* rear mic */ - { 0x19, PIN_VREF50 }, /* front mic */ - {} - } - }, - [ALC888VD_FIXUP_MIC_100VREF] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, PIN_VREF100 }, /* headset mic */ - {} - } - }, -}; - -static const struct hda_quirk alc882_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), - SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", - ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), - SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", - ALC882_FIXUP_ACER_ASPIRE_4930G), - SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), - SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), - SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), - SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), - SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), - SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), - SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF), - SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), - SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), - SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP), - - /* All Apple entries are in codec SSIDs */ - SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF), - SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), - SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), - SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), - SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), - SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF), - SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), - SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF), - - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF), - SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), - SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570), - SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), - SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), - SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), - SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), - SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x5802, "Clevo X58[05]WN[RST]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED), - SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), - SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), - {} -}; - -static const struct hda_model_fixup alc882_fixup_models[] = { - {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, - {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, - {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, - {.id = ALC889_FIXUP_CD, .name = "cd"}, - {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, - {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, - {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, - {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, - {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, - {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, - {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, - {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, - {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, - {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, - {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, - {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, - {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, - {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, - {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, - {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, - {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, - {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, - {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, - {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, - {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, - {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, - {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, - {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"}, - {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, - {} -}; - -static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01014010}, - {0x15, 0x01011012}, - {0x16, 0x01016011}, - {0x18, 0x01a19040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181304f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01456130}), - SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950, - {0x14, 0x01015010}, - {0x15, 0x01011012}, - {0x16, 0x01011011}, - {0x18, 0x01a11040}, - {0x19, 0x02a19050}, - {0x1a, 0x0181104f}, - {0x1b, 0x0221401f}, - {0x1e, 0x01451130}), - {} -}; - -/* - * BIOS auto configuration - */ -/* almost identical with ALC880 parser... */ -static int alc882_parse_auto_config(struct hda_codec *codec) +static void alc280_fixup_hp_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); + struct alc_spec *spec = codec->spec; + + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cap_mute_led_nid = 0x18; + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); + codec->power_filter = led_power_filter; + } } -/* +/* HP Spectre x360 14 model needs a unique workaround for enabling the amp; + * it needs to toggle the GPIO0 once on and off at each time (bko#210633) */ -static int patch_alc882(struct hda_codec *codec) +static void alc245_fixup_hp_x360_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; + struct alc_spec *spec = codec->spec; - switch (codec->core.vendor_id) { - case 0x10ec0882: - case 0x10ec0885: - case 0x10ec0900: - case 0x10ec0b00: - case 0x10ec1220: + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gpio_mask |= 0x01; + spec->gpio_dir |= 0x01; break; - default: - /* ALC883 and variants */ - alc_fix_pll_init(codec, 0x20, 0x0a, 10); + case HDA_FIXUP_ACT_INIT: + /* need to toggle GPIO to enable the amp */ + alc_update_gpio_data(codec, 0x01, true); + msleep(100); + alc_update_gpio_data(codec, 0x01, false); break; } +} - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, - alc882_fixups); - snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; +/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */ +static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + alc_update_gpio_data(codec, 0x04, true); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + alc_update_gpio_data(codec, 0x04, false); + break; } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; } - -/* - * ALC262 support - */ -static int alc262_parse_auto_config(struct hda_codec *codec) +static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - static const hda_nid_t alc262_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids); -} + struct alc_spec *spec = codec->spec; -/* - * Pin config fixes - */ -enum { - ALC262_FIXUP_FSC_H270, - ALC262_FIXUP_FSC_S7110, - ALC262_FIXUP_HP_Z200, - ALC262_FIXUP_TYAN, - ALC262_FIXUP_LENOVO_3000, - ALC262_FIXUP_BENQ, - ALC262_FIXUP_BENQ_T31, - ALC262_FIXUP_INV_DMIC, - ALC262_FIXUP_INTEL_BAYLEYBAY, -}; + if (action == HDA_FIXUP_ACT_PROBE) { + spec->gpio_mask |= 0x04; + spec->gpio_dir |= 0x04; + spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook; + } +} -static const struct hda_fixup alc262_fixups[] = { - [ALC262_FIXUP_FSC_H270] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0221142f }, /* front HP */ - { 0x1b, 0x0121141f }, /* rear HP */ - { } - } - }, - [ALC262_FIXUP_FSC_S7110] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x90170110 }, /* speaker */ - { } - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_HP_Z200] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x99130120 }, /* internal speaker */ - { } - } - }, - [ALC262_FIXUP_TYAN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x1993e1f0 }, /* int AUX */ - { } - } - }, - [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, PIN_VREF50 }, - {} - }, - .chained = true, - .chain_id = ALC262_FIXUP_BENQ, - }, - [ALC262_FIXUP_BENQ] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, - {} - } - }, - [ALC262_FIXUP_BENQ_T31] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, - {} - } - }, - [ALC262_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC262_FIXUP_INTEL_BAYLEYBAY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_depop_delay, - }, -}; +static void alc_update_coef_led(struct hda_codec *codec, + struct alc_coef_led *led, + bool polarity, bool on) +{ + if (polarity) + on = !on; + /* temporarily power up/down for setting COEF bit */ + alc_update_coef_idx(codec, led->idx, led->mask, + on ? led->on : led->off); +} -static const struct hda_quirk alc262_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), - SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), - SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), - SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000), - SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ), - SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31), - SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY), - {} -}; +/* update mute-LED according to the speaker mute state via COEF bit */ +static int coef_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; -static const struct hda_model_fixup alc262_fixup_models[] = { - {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, - {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, - {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, - {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, - {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, - {.id = ALC262_FIXUP_BENQ, .name = "benq"}, - {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, - {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, - {} -}; + alc_update_coef_led(codec, &spec->mute_led_coef, + spec->mute_led_polarity, brightness); + return 0; +} -/* - */ -static int patch_alc262(struct hda_codec *codec) +static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; + struct alc_spec *spec = codec->spec; - spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 1 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 0; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} - spec->shutup = alc_eapd_shutup; +static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80); -#endif - alc_fix_pll_init(codec, 0x20, 0x0a, 10); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x34; + spec->mute_led_coef.mask = 1 << 5; + spec->mute_led_coef.on = 0; + spec->mute_led_coef.off = 1 << 5; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} - alc_pre_init(codec); +static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, - alc262_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x07; + spec->mute_led_coef.mask = 1; + spec->mute_led_coef.on = 1; + spec->mute_led_coef.off = 0; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} - alc_auto_parse_customize_define(codec); +static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 3 << 2; + spec->mute_led_coef.on = 2 << 2; + spec->mute_led_coef.off = 1 << 2; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} - /* automatic parse from the BIOS config */ - err = alc262_parse_auto_config(codec); - if (err < 0) - goto error; +static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; - if (!spec->gen.no_analog && spec->gen.beep_nid) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 1 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 0; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); } +} - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); +/* turn on/off mic-mute LED per capture hook by coef bit */ +static int coef_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + alc_update_coef_led(codec, &spec->mic_led_coef, + spec->micmute_led_polarity, brightness); return 0; - - error: - alc_free(codec); - return err; } -/* - * ALC268 - */ -/* bind Beep switches of both NID 0x0f and 0x10 */ -static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned long pval; - int err; + struct alc_spec *spec = codec->spec; - mutex_lock(&codec->control_mutex); - pval = kcontrol->private_value; - kcontrol->private_value = (pval & ~0xff) | 0x0f; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (err >= 0) { - kcontrol->private_value = (pval & ~0xff) | 0x10; - err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mic_led_coef.idx = 0x19; + spec->mic_led_coef.mask = 1 << 13; + spec->mic_led_coef.on = 1 << 13; + spec->mic_led_coef.off = 0; + snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); } - kcontrol->private_value = pval; - mutex_unlock(&codec->control_mutex); - return err; } -static const struct snd_kcontrol_new alc268_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Beep Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = alc268_beep_switch_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) - }, -}; +static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; -/* set PCBEEP vol = 0, mute connections */ -static const struct hda_verb alc268_beep_init_verbs[] = { - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } -}; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->micmute_led_polarity = 1; + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); +} -enum { - ALC268_FIXUP_INV_DMIC, - ALC268_FIXUP_HP_EAPD, - ALC268_FIXUP_SPDIF, -}; +static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; -static const struct hda_fixup alc268_fixups[] = { - [ALC268_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC268_FIXUP_HP_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC268_FIXUP_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1e, 0x014b1180 }, /* enable SPDIF out */ - {} - } - }, -}; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mic_led_coef.idx = 0x35; + spec->mic_led_coef.mask = 3 << 2; + spec->mic_led_coef.on = 2 << 2; + spec->mic_led_coef.off = 1 << 2; + snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); + } +} -static const struct hda_model_fixup alc268_fixup_models[] = { - {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, - {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, - {} -}; +static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; -static const struct hda_quirk alc268_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), - SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), - /* below is codec SSID since multiple Toshiba laptops have the - * same PCI SSID 1179:ff00 - */ - SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), - {} -}; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0xb; + spec->mute_led_coef.mask = 3 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 1 << 4; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} -/* - * BIOS auto configuration - */ -static int alc268_parse_auto_config(struct hda_codec *codec) +static void alc285_fixup_hp_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, NULL, alc268_ssids); + alc285_fixup_hp_mute_led_coefbit(codec, fix, action); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); } -/* - */ -static int patch_alc268(struct hda_codec *codec) +static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec; - int i, err; - - /* ALC268 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; + alc285_fixup_hp_mute_led_coefbit(codec, fix, action); + alc285_fixup_hp_gpio_micmute_led(codec, fix, action); +} - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; +static void alc236_fixup_hp_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc236_fixup_hp_mute_led_coefbit(codec, fix, action); + alc236_fixup_hp_coef_micmute_led(codec, fix, action); +} - spec->shutup = alc_eapd_shutup; +static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - alc_pre_init(codec); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cap_mute_led_nid = 0x1a; + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); + codec->power_filter = led_power_filter; + } +} - snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); +static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc236_fixup_hp_mute_led_coefbit(codec, fix, action); + alc236_fixup_hp_micmute_led_vref(codec, fix, action); +} - /* automatic parse from the BIOS config */ - err = alc268_parse_auto_config(codec); - if (err < 0) - goto error; +static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec, + const unsigned short coefs[2]) +{ + alc_write_coef_idx(codec, 0x23, coefs[0]); + alc_write_coef_idx(codec, 0x25, coefs[1]); + alc_write_coef_idx(codec, 0x26, 0xb011); +} - if (err > 0 && !spec->gen.no_analog && - spec->gen.autocfg.speaker_pins[0] != 0x1d) { - for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &alc268_beep_mixer[i])) { - err = -ENOMEM; - goto error; - } - } - snd_hda_add_verbs(codec, alc268_beep_init_verbs); - if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) - /* override the amp caps for beep generator */ - snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, - (0x0c << AC_AMPCAP_OFFSET_SHIFT) | - (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } +struct alc298_samsung_amp_desc { + unsigned char nid; + unsigned short init_seq[2][2]; +}; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); +static void alc298_fixup_samsung_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + int i, j; + static const unsigned short init_seq[][2] = { + { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 }, + { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 }, + { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e }, + { 0x41, 0x07 }, { 0x400, 0x1 } + }; + static const struct alc298_samsung_amp_desc amps[] = { + { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } }, + { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } } + }; - return 0; + if (action != HDA_FIXUP_ACT_INIT) + return; - error: - alc_free(codec); - return err; -} + for (i = 0; i < ARRAY_SIZE(amps); i++) { + alc_write_coef_idx(codec, 0x22, amps[i].nid); -/* - * ALC269 - */ + for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++) + alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]); -static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ -}; + for (j = 0; j < ARRAY_SIZE(init_seq); j++) + alc298_samsung_write_coef_pack(codec, init_seq[j]); + } +} -static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { - .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ +struct alc298_samsung_v2_amp_desc { + unsigned short nid; + int init_seq_size; + unsigned short init_seq[18][2]; }; -/* different alc269-variants */ -enum { - ALC269_TYPE_ALC269VA, - ALC269_TYPE_ALC269VB, - ALC269_TYPE_ALC269VC, - ALC269_TYPE_ALC269VD, - ALC269_TYPE_ALC280, - ALC269_TYPE_ALC282, - ALC269_TYPE_ALC283, - ALC269_TYPE_ALC284, - ALC269_TYPE_ALC293, - ALC269_TYPE_ALC286, - ALC269_TYPE_ALC298, - ALC269_TYPE_ALC255, - ALC269_TYPE_ALC256, - ALC269_TYPE_ALC257, - ALC269_TYPE_ALC215, - ALC269_TYPE_ALC225, - ALC269_TYPE_ALC245, - ALC269_TYPE_ALC287, - ALC269_TYPE_ALC294, - ALC269_TYPE_ALC300, - ALC269_TYPE_ALC623, - ALC269_TYPE_ALC700, +static const struct alc298_samsung_v2_amp_desc +alc298_samsung_v2_amp_desc_tbl[] = { + { 0x38, 18, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, + { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, + { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } + }}, + { 0x39, 18, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, + { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, + { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } + }}, + { 0x3c, 15, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, + { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } + }}, + { 0x3d, 15, { + { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, + { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, + { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, + { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, + { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } + }} }; -/* - * BIOS auto configuration - */ -static int alc269_parse_auto_config(struct hda_codec *codec) +static void alc298_samsung_v2_enable_amps(struct hda_codec *codec) { - static const hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; struct alc_spec *spec = codec->spec; - const hda_nid_t *ssids; + static const unsigned short enable_seq[][2] = { + { 0x203a, 0x0081 }, { 0x23ff, 0x0001 }, + }; + int i, j; - switch (spec->codec_variant) { - case ALC269_TYPE_ALC269VA: - case ALC269_TYPE_ALC269VC: - case ALC269_TYPE_ALC280: - case ALC269_TYPE_ALC284: - case ALC269_TYPE_ALC293: - ssids = alc269va_ssids; - break; - case ALC269_TYPE_ALC269VB: - case ALC269_TYPE_ALC269VD: - case ALC269_TYPE_ALC282: - case ALC269_TYPE_ALC283: - case ALC269_TYPE_ALC286: - case ALC269_TYPE_ALC298: - case ALC269_TYPE_ALC255: - case ALC269_TYPE_ALC256: - case ALC269_TYPE_ALC257: - case ALC269_TYPE_ALC215: - case ALC269_TYPE_ALC225: - case ALC269_TYPE_ALC245: - case ALC269_TYPE_ALC287: - case ALC269_TYPE_ALC294: - case ALC269_TYPE_ALC300: - case ALC269_TYPE_ALC623: - case ALC269_TYPE_ALC700: - ssids = alc269_ssids; - break; - default: - ssids = alc269_ssids; - break; + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < ARRAY_SIZE(enable_seq); j++) + alc298_samsung_write_coef_pack(codec, enable_seq[j]); + codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); } - - return alc_parse_auto_config(codec, alc269_ignore, ssids); } -static const struct hda_jack_keymap alc_headset_btn_keymap[] = { - { SND_JACK_BTN_0, KEY_PLAYPAUSE }, - { SND_JACK_BTN_1, KEY_VOICECOMMAND }, - { SND_JACK_BTN_2, KEY_VOLUMEUP }, - { SND_JACK_BTN_3, KEY_VOLUMEDOWN }, - {} -}; - -static void alc_headset_btn_callback(struct hda_codec *codec, - struct hda_jack_callback *jack) +static void alc298_samsung_v2_disable_amps(struct hda_codec *codec) { - int report = 0; - - if (jack->unsol_res & (7 << 13)) - report |= SND_JACK_BTN_0; - - if (jack->unsol_res & (1 << 16 | 3 << 8)) - report |= SND_JACK_BTN_1; - - /* Volume up key */ - if (jack->unsol_res & (7 << 23)) - report |= SND_JACK_BTN_2; + struct alc_spec *spec = codec->spec; + static const unsigned short disable_seq[][2] = { + { 0x23ff, 0x0000 }, { 0x203a, 0x0080 }, + }; + int i, j; - /* Volume down key */ - if (jack->unsol_res & (7 << 10)) - report |= SND_JACK_BTN_3; + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < ARRAY_SIZE(disable_seq); j++) + alc298_samsung_write_coef_pack(codec, disable_seq[j]); + codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); + } +} - snd_hda_jack_set_button_state(codec, jack->nid, report); +static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + /* Dynamically enable/disable speaker amps before and after playback */ + if (action == HDA_GEN_PCM_ACT_OPEN) + alc298_samsung_v2_enable_amps(codec); + if (action == HDA_GEN_PCM_ACT_CLOSE) + alc298_samsung_v2_disable_amps(codec); } -static void alc_disable_headset_jack_key(struct hda_codec *codec) +static void alc298_samsung_v2_init_amps(struct hda_codec *codec, + int num_speaker_amps) { struct alc_spec *spec = codec->spec; + int i, j; - if (!spec->has_hs_key) - return; + /* Set spec's num_speaker_amps before doing anything else */ + spec->num_speaker_amps = num_speaker_amps; - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0287: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_write_coef_idx(codec, 0x48, 0x0); - alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); - alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_write_coef_idx(codec, 0x48, 0x0); - alc_update_coef_idx(codec, 0x49, 0x0045, 0x0); - break; + /* Disable speaker amps before init to prevent any physical damage */ + alc298_samsung_v2_disable_amps(codec); + + /* Initialize the speaker amps */ + for (i = 0; i < spec->num_speaker_amps; i++) { + alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); + for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) { + alc298_samsung_write_coef_pack(codec, + alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]); + } + alc_write_coef_idx(codec, 0x89, 0x0); + codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n", + alc298_samsung_v2_amp_desc_tbl[i].nid); } + + /* register hook to enable speaker amps only when they are needed */ + spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook; } -static void alc_enable_headset_jack_key(struct hda_codec *codec) +static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - - if (!spec->has_hs_key) - return; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0287: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_write_coef_idx(codec, 0x48, 0xd011); - alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045); - break; - } + if (action == HDA_FIXUP_ACT_PROBE) + alc298_samsung_v2_init_amps(codec, 2); } -static void alc_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->has_hs_key = 1; - snd_hda_jack_detect_enable_callback(codec, 0x55, - alc_headset_btn_callback); - break; - case HDA_FIXUP_ACT_BUILD: - hp_pin = alc_get_hp_pin(spec); - if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55, - alc_headset_btn_keymap, - hp_pin)) - snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack", - false, SND_JACK_HEADSET, - alc_headset_btn_keymap); - - alc_enable_headset_jack_key(codec); - break; - } + if (action == HDA_FIXUP_ACT_PROBE) + alc298_samsung_v2_init_amps(codec, 4); } -static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up) +static void gpio2_mic_hotkey_event(struct hda_codec *codec, + struct hda_jack_callback *event) { - alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0); + struct alc_spec *spec = codec->spec; + + /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore + send both key on and key off event for every interrupt. */ + input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1); + input_sync(spec->kb_dev); + input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0); + input_sync(spec->kb_dev); } -static void alc269_shutup(struct hda_codec *codec) +static int alc_register_micmute_input_device(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + int i; - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 0); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x018) { - msleep(150); + spec->kb_dev = input_allocate_device(); + if (!spec->kb_dev) { + codec_err(codec, "Out of memory (input_allocate_device)\n"); + return -ENOMEM; } - alc_shutup_pins(codec); -} -static const struct coef_fw alc282_coefs[] = { - WRITE_COEF(0x03, 0x0002), /* Power Down Control */ - UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ - WRITE_COEF(0x07, 0x0200), /* DMIC control */ - UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ - UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ - WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ - WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ - WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */ - UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ - UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ - WRITE_COEF(0x6f, 0x0), /* Class D test 4 */ - UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */ - WRITE_COEF(0x34, 0xa0c0), /* ANC */ - UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */ - UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ - UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ - WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ - WRITE_COEF(0x63, 0x2902), /* PLL */ - WRITE_COEF(0x68, 0xa080), /* capless control 2 */ - WRITE_COEF(0x69, 0x3400), /* capless control 3 */ - WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */ - WRITE_COEF(0x6b, 0x0), /* capless control 5 */ - UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */ - WRITE_COEF(0x6e, 0x110a), /* class D test 3 */ - UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */ - WRITE_COEF(0x71, 0x0014), /* class D test 6 */ - WRITE_COEF(0x72, 0xc2ba), /* classD OCP */ - UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */ - WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */ - {} -}; + spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE; -static void alc282_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc282_coefs); + spec->kb_dev->name = "Microphone Mute Button"; + spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); + spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]); + spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map); + spec->kb_dev->keycode = spec->alc_mute_keycode_map; + for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++) + set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit); + + if (input_register_device(spec->kb_dev)) { + codec_err(codec, "input_register_device failed\n"); + input_free_device(spec->kb_dev); + spec->kb_dev = NULL; + return -ENOMEM; + } + + return 0; } -static void alc282_init(struct hda_codec *codec) +/* GPIO1 = set according to SKU external amp + * GPIO2 = mic mute hotkey + * GPIO3 = mute LED + * GPIO4 = mic mute LED + */ +static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - int coef78; - alc282_restore_default_value(codec); + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->init_amp = ALC_INIT_DEFAULT; + if (alc_register_micmute_input_device(codec) != 0) + return; - if (!hp_pin) + spec->gpio_mask |= 0x06; + spec->gpio_dir |= 0x02; + spec->gpio_data |= 0x02; + snd_hda_codec_write_cache(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); + snd_hda_jack_detect_enable_callback(codec, codec->core.afg, + gpio2_mic_hotkey_event); return; - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - coef78 = alc_read_coef_idx(codec, 0x78); + } - /* Index 0x78 Direct Drive HP AMP LPM Control 1 */ - /* Headphone capless set to high power mode */ - alc_write_coef_idx(codec, 0x78, 0x9004); + if (!spec->kb_dev) + return; - if (hp_pin_sense) - msleep(2); + switch (action) { + case HDA_FIXUP_ACT_FREE: + input_unregister_device(spec->kb_dev); + spec->kb_dev = NULL; + } +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +/* Line2 = mic mute hotkey + * GPIO2 = mic mute LED + */ +static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - if (hp_pin_sense) - msleep(85); + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->init_amp = ALC_INIT_DEFAULT; + if (alc_register_micmute_input_device(codec) != 0) + return; - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_jack_detect_enable_callback(codec, 0x1b, + gpio2_mic_hotkey_event); + return; + } - if (hp_pin_sense) - msleep(100); + if (!spec->kb_dev) + return; - /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x78, coef78); + switch (action) { + case HDA_FIXUP_ACT_FREE: + input_unregister_device(spec->kb_dev); + spec->kb_dev = NULL; + } } -static void alc282_shutup(struct hda_codec *codec) +static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - int coef78; - if (!hp_pin) { - alc269_shutup(codec); - return; + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cap_mute_led_nid = 0x18; + snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); } +} - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - coef78 = alc_read_coef_idx(codec, 0x78); - alc_write_coef_idx(codec, 0x78, 0x9004); +static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - if (hp_pin_sense) - msleep(2); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->micmute_led_polarity = 1; + alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +static void alc255_set_default_jack_type(struct hda_codec *codec) +{ + /* Set to iphone type */ + static const struct coef_fw alc255fw[] = { + WRITE_COEF(0x1b, 0x880b), + WRITE_COEF(0x45, 0xd089), + WRITE_COEF(0x1b, 0x080b), + WRITE_COEF(0x46, 0x0004), + WRITE_COEF(0x1b, 0x0c0b), + {} + }; + static const struct coef_fw alc256fw[] = { + WRITE_COEF(0x1b, 0x884b), + WRITE_COEF(0x45, 0xd089), + WRITE_COEF(0x1b, 0x084b), + WRITE_COEF(0x46, 0x0004), + WRITE_COEF(0x1b, 0x0c4b), + {} + }; + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, alc255fw); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_process_coef_fw(codec, alc256fw); + break; + } + msleep(30); +} - if (hp_pin_sense) - msleep(85); +static void alc_fixup_headset_mode_alc255(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + alc255_set_default_jack_type(codec); + } + alc_fixup_headset_mode(codec, fix, action); +} - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); +static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + alc255_set_default_jack_type(codec); + } + else + alc_fixup_headset_mode(codec, fix, action); +} - if (hp_pin_sense) - msleep(100); +static void alc288_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x78, coef78); + alc_update_headset_jack_cb(codec, jack); + /* Headset Mic enable or disable, only for Dell Dino */ + alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present); } -static const struct coef_fw alc283_coefs[] = { - WRITE_COEF(0x03, 0x0002), /* Power Down Control */ - UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */ - WRITE_COEF(0x07, 0x0200), /* DMIC control */ - UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */ - UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */ - WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */ - WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */ - WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */ - UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */ - UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */ - WRITE_COEF(0x3a, 0x0), /* Class D test 4 */ - UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */ - WRITE_COEF(0x22, 0xa0c0), /* ANC */ - UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */ - UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */ - UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */ - WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */ - WRITE_COEF(0x2e, 0x2902), /* PLL */ - WRITE_COEF(0x33, 0xa080), /* capless control 2 */ - WRITE_COEF(0x34, 0x3400), /* capless control 3 */ - WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */ - WRITE_COEF(0x36, 0x0), /* capless control 5 */ - UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */ - WRITE_COEF(0x39, 0x110a), /* class D test 3 */ - UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */ - WRITE_COEF(0x3c, 0x0014), /* class D test 6 */ - WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */ - UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */ - WRITE_COEF(0x49, 0x0), /* test mode */ - UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */ - UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */ - WRITE_COEF(0x37, 0xfc06), /* Class D amp control */ - UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */ - {} -}; +static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_headset_mode(codec, fix, action); + if (action == HDA_FIXUP_ACT_PROBE) { + struct alc_spec *spec = codec->spec; + /* toggled via hp_automute_hook */ + spec->gpio_mask |= 0x40; + spec->gpio_dir |= 0x40; + spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; + } +} -static void alc283_restore_default_value(struct hda_codec *codec) +static void alc_fixup_no_shutup(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - alc_process_coef_fw(codec, alc283_coefs); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->no_shutup_pins = 1; + } } -static void alc283_init(struct hda_codec *codec) +/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ +static void alc_fixup_tpt440_dock(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { + static const struct hda_pintbl pincfgs[] = { + { 0x16, 0x21211010 }, /* dock headphone */ + { 0x19, 0x21a11010 }, /* dock mic */ + { } + }; struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - alc283_restore_default_value(codec); - if (!hp_pin) - return; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + codec->power_save_node = 0; /* avoid click noises */ + snd_hda_apply_pincfgs(codec, pincfgs); + } +} - msleep(30); - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); +static void alc_fixup_tpt470_dock(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const struct hda_pintbl pincfgs[] = { + { 0x17, 0x21211010 }, /* dock headphone */ + { 0x19, 0x21a11010 }, /* dock mic */ + { } + }; + struct alc_spec *spec = codec->spec; - /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ - /* Headphone capless set to high power mode */ - alc_write_coef_idx(codec, 0x43, 0x9004); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + snd_hda_apply_pincfgs(codec, pincfgs); + } else if (action == HDA_FIXUP_ACT_INIT) { + /* Enable DOCK device */ + snd_hda_codec_write(codec, 0x17, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); + /* Enable DOCK device */ + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); + } +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +static void alc_fixup_tpt470_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise + * the speaker output becomes too low by some reason on Thinkpads with + * ALC298 codec + */ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, + 0 + }; + struct alc_spec *spec = codec->spec; - if (hp_pin_sense) - msleep(85); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.preferred_dacs = preferred_pairs; +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +static void alc295_fixup_asus_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t preferred_pairs[] = { + 0x17, 0x02, 0x21, 0x03, 0 + }; + struct alc_spec *spec = codec->spec; - if (hp_pin_sense) - msleep(85); - /* Index 0x46 Combo jack auto switch control 2 */ - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x43, 0x9614); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.preferred_dacs = preferred_pairs; } -static void alc283_shutup(struct hda_codec *codec) +static void alc271_hp_gate_mic_jack(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - if (!hp_pin) { - alc269_shutup(codec); - return; - } + if (action == HDA_FIXUP_ACT_PROBE) { + int mic_pin = alc_find_ext_mic_pin(codec); + int hp_pin = alc_get_hp_pin(spec); - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + if (snd_BUG_ON(!mic_pin || !hp_pin)) + return; + snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin); + } +} - alc_write_coef_idx(codec, 0x43, 0x9004); +static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int i; - /*depop hp during suspend*/ - alc_write_coef_idx(codec, 0x06, 0x2100); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - if (hp_pin_sense) - msleep(100); + /* The mic boosts on level 2 and 3 are too noisy + on the internal mic input. + Therefore limit the boost to 0 or 1. */ - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (action != HDA_FIXUP_ACT_PROBE) + return; - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + unsigned int defcfg; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) + continue; - if (hp_pin_sense) - msleep(100); - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x43, 0x9614); + snd_hda_override_amp_caps(codec, nid, HDA_INPUT, + (0x00 << AC_AMPCAP_OFFSET_SHIFT) | + (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + } } -static void alc256_init(struct hda_codec *codec) +static void alc283_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (spec->ultra_low_power) { - alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1); - alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2); - alc_update_coef_idx(codec, 0x08, 7<<4, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 0); - alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); - msleep(30); - } - - if (!hp_pin) - hp_pin = 0x21; + int vref; - msleep(30); + msleep(200); + snd_hda_gen_hp_automute(codec, jack); - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - if (hp_pin_sense) { - msleep(2); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + msleep(600); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +static void alc283_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - msleep(75); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_wcaps(codec, 0x03, 0); + /* Disable AA-loopback as it causes white noise */ + spec->gen.mixer_nid = 0; + break; + case HDA_FIXUP_ACT_INIT: + /* MIC2-VREF control */ + /* Set to manual mode */ + alc_update_coef_idx(codec, 0x06, 0x000c, 0); + /* Enable Line1 input control by verb */ + alc_update_coef_idx(codec, 0x1a, 0, 1 << 4); + break; + } +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +static void alc283_fixup_sense_combo_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; - msleep(75); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.hp_automute_hook = alc283_hp_automute_hook; + break; + case HDA_FIXUP_ACT_INIT: + /* MIC2-VREF control */ + /* Set to manual mode */ + alc_update_coef_idx(codec, 0x06, 0x000c, 0); + break; } - alc_update_coef_idx(codec, 0x46, 3 << 12, 0); - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ - alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); - /* - * Expose headphone mic (or possibly Line In on some machines) instead - * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See - * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of - * this register. - */ - alc_write_coef_idx(codec, 0x36, 0x5757); } -static void alc256_shutup(struct hda_codec *codec) +/* mute tablet speaker pin (0x14) via dock plugging in addition */ +static void asus_tx300_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; + snd_hda_gen_update_outputs(codec); + if (snd_hda_jack_detect(codec, 0x1b)) + spec->gen.mute_bits |= (1ULL << 0x14); +} - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); +static void alc282_fixup_asus_tx300(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl dock_pins[] = { + { 0x1b, 0x21114000 }, /* dock speaker pin */ + {} + }; - if (hp_pin_sense) { - msleep(2); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->init_amp = ALC_INIT_DEFAULT; + /* TX300 needs to set up GPIO2 for the speaker amp */ + alc_setup_gpio(codec, 0x04); + snd_hda_apply_pincfgs(codec, dock_pins); + spec->gen.auto_mute_via_amp = 1; + spec->gen.automute_hook = asus_tx300_automute; + snd_hda_jack_detect_enable_callback(codec, 0x1b, + snd_hda_gen_hp_automute); + break; + case HDA_FIXUP_ACT_PROBE: + spec->init_amp = ALC_INIT_DEFAULT; + break; + case HDA_FIXUP_ACT_BUILD: + /* this is a bit tricky; give more sane names for the main + * (tablet) speaker and the dock speaker, respectively + */ + rename_ctl(codec, "Speaker Playback Switch", + "Dock Speaker Playback Switch"); + rename_ctl(codec, "Bass Speaker Playback Switch", + "Speaker Playback Switch"); + break; + } +} - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); +static void alc290_fixup_mono_speakers(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* DAC node 0x03 is giving mono output. We therefore want to + make sure 0x14 (front speaker) and 0x15 (headphones) use the + stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */ + static const hda_nid_t conn1[] = { 0x0c }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); + } +} - msleep(75); +static void alc298_fixup_speaker_volume(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* The speaker is routed to the Node 0x06 by a mistake, as a result + we can't adjust the speaker's volume since this node does not has + Amp-out capability. we change the speaker's route to: + Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 ( + Pin Complex), since Node 0x02 has Amp-out caps, we can adjust + speaker's volume now. */ - /* 3k pull low control for Headset jack. */ - /* NOTE: call this before clearing the pin, otherwise codec stalls */ - /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly - * when booting with headset plugged. So skip setting it for the codec alc257 - */ - if (spec->en_3kpull_low) - alc_update_coef_idx(codec, 0x46, 0, 3 << 12); + static const hda_nid_t conn1[] = { 0x0c }; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1); + } +} - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); +/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */ +static void alc295_fixup_disable_dac3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + } +} - msleep(75); +/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */ +static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02 }; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); } +} - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - if (spec->ultra_low_power) { - msleep(50); - alc_update_coef_idx(codec, 0x03, 1<<1, 0); - alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4); - alc_update_coef_idx(codec, 0x08, 3<<2, 0); - alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15); - alc_update_coef_idx(codec, 0x0e, 7<<6, 0); - msleep(30); +/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ +static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + snd_hda_gen_add_micmute_led_cdev(codec, NULL); } } -static void alc285_hp_init(struct hda_codec *codec) +/* Hook to update amp GPIO4 for automute */ +static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - int i, val; - int coef38, coef0d, coef36; - - alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */ - alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */ - coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */ - coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */ - coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */ - alc_update_coef_idx(codec, 0x38, 1<<4, 0x0); - alc_update_coef_idx(codec, 0x0d, 0x110, 0x0); - - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - if (hp_pin) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(130); - alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14); - alc_update_coef_idx(codec, 0x36, 1<<13, 0x0); - - if (hp_pin) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(10); - alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */ - alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880); - alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049); - alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0); + snd_hda_gen_hp_automute(codec, jack); + /* mute_led_polarity is set to 0, so we pass inverted value here */ + alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity, + !spec->gen.hp_jack_present); +} - alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */ - val = alc_read_coefex_idx(codec, 0x58, 0x00); - for (i = 0; i < 20 && val & 0x8000; i++) { - msleep(50); - val = alc_read_coefex_idx(codec, 0x58, 0x00); - } /* Wait for depop procedure finish */ +/* Manage GPIOs for HP EliteBook Folio 9480m. + * + * GPIO4 is the headphone amplifier power control + * GPIO3 is the audio output mute indicator LED + */ - alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ - alc_update_coef_idx(codec, 0x38, 1<<4, coef38); - alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); - alc_update_coef_idx(codec, 0x36, 3<<13, coef36); +static void alc280_fixup_hp_9480m(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; - msleep(50); - alc_update_coef_idx(codec, 0x4a, 1<<15, 0); + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */ + spec->gpio_mask |= 0x10; + spec->gpio_dir |= 0x10; + spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook; + } } -static void alc225_init(struct hda_codec *codec) +static void alc275_fixup_gpio4_off(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - if (spec->ultra_low_power) { - alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2); - alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6); - alc_update_coef_idx(codec, 0x33, 1<<11, 0); - msleep(30); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gpio_mask |= 0x04; + spec->gpio_dir |= 0x04; + /* set data bit low */ } +} - if (spec->codec_variant != ALC269_TYPE_ALC287 && - spec->codec_variant != ALC269_TYPE_ALC245) - /* required only at boot or S3 and S4 resume time */ - if (!spec->done_hp_init || - is_s3_resume(codec) || - is_s4_resume(codec)) { - alc285_hp_init(codec); - spec->done_hp_init = true; - } +/* Quirk for Thinkpad X1 7th and 8th Gen + * The following fixed routing needed + * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly + * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC + * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp + */ +static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0 + }; + struct alc_spec *spec = codec->spec; - if (!hp_pin) - hp_pin = 0x21; - msleep(30); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; + break; + case HDA_FIXUP_ACT_BUILD: + /* The generic parser creates somewhat unintuitive volume ctls + * with the fixed routing above, and the shared DAC2 may be + * confusing for PA. + * Rename those to unique names so that PA doesn't touch them + * and use only Master volume. + */ + rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume"); + rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume"); + break; + } +} - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); +static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */ + codec->power_save_node = 1; +} - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(75); +/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ +static void alc274_fixup_bind_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t preferred_pairs[] = { + 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02, + 0 + }; - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - msleep(75); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ - } + spec->gen.preferred_dacs = preferred_pairs; + spec->gen.auto_mute_via_amp = 1; + codec->power_save_node = 0; } -static void alc225_shutup(struct hda_codec *codec) +/* avoid DAC 0x06 for speaker switch 0x17; it has no volume control */ +static void alc274_fixup_hp_aio_bind_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { + static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ + /* The speaker is routed to the Node 0x06 by a mistake, thus the + * speaker's volume can't be adjusted since the node doesn't have + * Amp-out capability. Assure the speaker and lineout pin to be + * coupled with DAC NID 0x02. + */ + static const hda_nid_t preferred_pairs[] = { + 0x16, 0x02, 0x17, 0x02, 0x21, 0x03, 0 + }; struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x16); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; +} - if (hp1_pin_sense || hp2_pin_sense) { - alc_disable_headset_jack_key(codec); - /* 3k pull low control for Headset jack. */ - alc_update_coef_idx(codec, 0x4a, 0, 3 << 10); - msleep(2); +/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */ +static void alc289_fixup_asus_ga401(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0 + }; + struct alc_spec *spec = codec->spec; - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.preferred_dacs = preferred_pairs; +} - msleep(75); +/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */ +static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x16, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + snd_hda_override_wcaps(codec, 0x03, 0); +} - msleep(75); - alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); - alc_enable_headset_jack_key(codec); - } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); - if (spec->ultra_low_power) { - msleep(50); - alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2); - alc_update_coef_idx(codec, 0x0e, 7<<6, 0); - alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11); - alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4); - msleep(30); +static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec) +{ + switch (codec->core.vendor_id) { + case 0x10ec0274: + case 0x10ec0294: + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */ + alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15); + break; + case 0x10ec0230: + case 0x10ec0235: + case 0x10ec0236: + case 0x10ec0255: + case 0x10ec0256: + case 0x10ec0257: + case 0x19e58326: + alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */ + alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15); + break; } } -static void alc222_init(struct hda_codec *codec) +static void alc295_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - return; - - msleep(30); - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); - - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - - msleep(75); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->ultra_low_power = true; + break; + case HDA_FIXUP_ACT_INIT: + alc_combo_jack_hp_jd_restart(codec); + break; } } -static void alc222_shutup(struct hda_codec *codec) +static void alc256_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp1_pin_sense, hp2_pin_sense; - - if (!hp_pin) - hp_pin = 0x21; - - hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); - hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); - - if (hp1_pin_sense || hp2_pin_sense) { - msleep(2); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - if (hp1_pin_sense) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - if (hp2_pin_sense) - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(75); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + if (codec->core.subsystem_id == 0x10280d76) + spec->gen.suppress_auto_mute = 0; + else + spec->gen.suppress_auto_mute = 1; + spec->gen.suppress_auto_mic = 1; + spec->en_3kpull_low = false; + break; } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); } -static void alc_default_init(struct hda_codec *codec) +static void alc_fixup_disable_mic_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; - - if (!hp_pin) - return; - - msleep(30); - - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); +} - if (hp_pin_sense) { - msleep(2); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +static void alc294_gx502_toggle_output(struct hda_codec *codec, + struct hda_jack_callback *cb) +{ + /* The Windows driver sets the codec up in a very different way where + * it appears to leave 0x10 = 0x8a20 set. For Linux we need to toggle it + */ + if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) + alc_write_coef_idx(codec, 0x10, 0x8a20); + else + alc_write_coef_idx(codec, 0x10, 0x0a20); +} - msleep(75); +static void alc294_fixup_gx502_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Pin 0x21: headphones/headset mic */ + if (!is_jack_detectable(codec, 0x21)) + return; - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - msleep(75); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x21, + alc294_gx502_toggle_output); + break; + case HDA_FIXUP_ACT_INIT: + /* Make sure to start in a correct state, i.e. if + * headphones have been plugged in before powering up the system + */ + alc294_gx502_toggle_output(codec, NULL); + break; } } -static void alc_default_shutup(struct hda_codec *codec) +static void alc294_gu502_toggle_output(struct hda_codec *codec, + struct hda_jack_callback *cb) { - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - bool hp_pin_sense; + /* Windows sets 0x10 to 0x8420 for Node 0x20 which is + * responsible from changes between speakers and headphones + */ + if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) + alc_write_coef_idx(codec, 0x10, 0x8420); + else + alc_write_coef_idx(codec, 0x10, 0x0a20); +} - if (!hp_pin) { - alc269_shutup(codec); +static void alc294_fixup_gu502_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (!is_jack_detectable(codec, 0x21)) return; - } - hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); - - if (hp_pin_sense) { - msleep(2); - - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - - msleep(75); - - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - msleep(75); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x21, + alc294_gu502_toggle_output); + break; + case HDA_FIXUP_ACT_INIT: + alc294_gu502_toggle_output(codec, NULL); + break; } - alc_auto_setup_eapd(codec, false); - alc_shutup_pins(codec); } -static void alc294_hp_init(struct hda_codec *codec) +static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - int i, val; - - if (!hp_pin) + if (action != HDA_FIXUP_ACT_INIT) return; - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(100); + alc_write_coef_idx(codec, 0x65, 0x0); +} - if (!spec->no_shutup_pins) - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - - alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ - alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ - - /* Wait for depop procedure finish */ - val = alc_read_coefex_idx(codec, 0x58, 0x01); - for (i = 0; i < 20 && val & 0x0080; i++) { - msleep(50); - val = alc_read_coefex_idx(codec, 0x58, 0x01); +static void alc274_fixup_hp_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + switch (action) { + case HDA_FIXUP_ACT_INIT: + alc_combo_jack_hp_jd_restart(codec); + break; } - /* Set HP depop to auto mode */ - alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b); - msleep(50); } -static void alc294_init(struct hda_codec *codec) +static void alc_fixup_no_int_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - /* required only at boot or S4 resume time */ - if (!spec->done_hp_init || - codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) { - alc294_hp_init(codec); - spec->done_hp_init = true; + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* Mic RING SLEEVE swap for combo jack */ + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + spec->no_internal_mic_pin = true; + break; + case HDA_FIXUP_ACT_INIT: + alc_combo_jack_hp_jd_restart(codec); + break; } - alc_default_init(codec); } -static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, - unsigned int val) +/* GPIO1 = amplifier on/off + * GPIO3 = mic mute LED + */ +static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */ - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */ -} + static const hda_nid_t conn[] = { 0x02 }; -static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg) -{ - unsigned int val; + struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, /* front/high speakers */ + { 0x17, 0x90170130 }, /* back/bass speakers */ + { } + }; - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); - val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) - & 0xffff; - val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) - << 16; - return val; -} + //enable micmute led + alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04); -static void alc5505_dsp_halt(struct hda_codec *codec) -{ - unsigned int val; - - alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */ - alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */ - alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */ - alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */ - alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */ - alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */ - alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */ - val = alc5505_coef_get(codec, 0x6220); - alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */ + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->micmute_led_polarity = 1; + /* needed for amp of back speakers */ + spec->gpio_mask |= 0x01; + spec->gpio_dir |= 0x01; + snd_hda_apply_pincfgs(codec, pincfgs); + /* share DAC to have unified volume control */ + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_INIT: + /* need to toggle GPIO to enable the amp of back speakers */ + alc_update_gpio_data(codec, 0x01, true); + msleep(100); + alc_update_gpio_data(codec, 0x01, false); + break; + } } -static void alc5505_dsp_back_from_halt(struct hda_codec *codec) +/* GPIO1 = amplifier on/off */ +static void alc285_fixup_hp_spectre_x360_df1(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - alc5505_coef_set(codec, 0x61b8, 0x04133302); - alc5505_coef_set(codec, 0x61b0, 0x00005b16); - alc5505_coef_set(codec, 0x61b4, 0x040a2b02); - alc5505_coef_set(codec, 0x6230, 0xf80d4011); - alc5505_coef_set(codec, 0x6220, 0x2002010f); - alc5505_coef_set(codec, 0x880c, 0x00000004); + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02 }; + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, /* front/high speakers */ + { 0x17, 0x90170130 }, /* back/bass speakers */ + { } + }; + + // enable mute led + alc285_fixup_hp_mute_led_coefbit(codec, fix, action); + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* needed for amp of back speakers */ + spec->gpio_mask |= 0x01; + spec->gpio_dir |= 0x01; + snd_hda_apply_pincfgs(codec, pincfgs); + /* share DAC to have unified volume control */ + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_INIT: + /* need to toggle GPIO to enable the amp of back speakers */ + alc_update_gpio_data(codec, 0x01, true); + msleep(100); + alc_update_gpio_data(codec, 0x01, false); + break; + } } -static void alc5505_dsp_init(struct hda_codec *codec) +static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - unsigned int val; + static const hda_nid_t conn[] = { 0x02 }; + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, /* rear speaker */ + { } + }; - alc5505_dsp_halt(codec); - alc5505_dsp_back_from_halt(codec); - alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */ - alc5505_coef_set(codec, 0x61b0, 0x5b16); - alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */ - alc5505_coef_set(codec, 0x61b4, 0x04132b02); - alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/ - alc5505_coef_set(codec, 0x61b8, 0x041f3302); - snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */ - alc5505_coef_set(codec, 0x61b8, 0x041b3302); - alc5505_coef_set(codec, 0x61b8, 0x04173302); - alc5505_coef_set(codec, 0x61b8, 0x04163302); - alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */ - alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */ - alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */ + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + /* force front speaker to DAC1 */ + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + } +} - val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */ - if (val <= 3) - alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */ - else - alc5505_coef_set(codec, 0x6220, 0x6002018f); +static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const struct coef_fw coefs[] = { + WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023), + WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03), + WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a), + WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014), + WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15), + WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489), + WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0), + WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000), + WRITE_COEF(0x6e, 0x1005), { } + }; - alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/ - alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */ - alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */ - alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */ - alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ - alc5505_coef_set(codec, 0x880c, 0x00000003); - alc5505_coef_set(codec, 0x880c, 0x00000010); + static const struct hda_pintbl pincfgs[] = { + { 0x12, 0xb7a60130 }, /* Internal microphone*/ + { 0x14, 0x90170150 }, /* B&O soundbar speakers */ + { 0x17, 0x90170153 }, /* Side speakers */ + { 0x19, 0x03a11040 }, /* Headset microphone */ + { } + }; -#ifdef HALT_REALTEK_ALC5505 - alc5505_dsp_halt(codec); -#endif -} + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); -#ifdef HALT_REALTEK_ALC5505 -#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */ -#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */ -#else -#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec) -#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec) -#endif + /* Fixes volume control problem for side speakers */ + alc295_fixup_disable_dac3(codec, fix, action); -static int alc269_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; + /* Fixes no sound from headset speaker */ + snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0); - if (spec->has_alc5505_dsp) - alc5505_dsp_suspend(codec); + /* Auto-enable headset mic when plugged */ + snd_hda_jack_set_gating_jack(codec, 0x19, 0x21); - return alc_suspend(codec); + /* Headset mic volume enhancement */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, coefs); + break; + case HDA_FIXUP_ACT_BUILD: + rename_ctl(codec, "Bass Speaker Playback Volume", + "B&O-Tuned Playback Volume"); + rename_ctl(codec, "Front Playback Switch", + "B&O Soundbar Playback Switch"); + rename_ctl(codec, "Bass Speaker Playback Switch", + "Side Speaker Playback Switch"); + break; + } } -static int alc269_resume(struct hda_codec *codec) +static void alc285_fixup_hp_beep(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 0); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x018) { - msleep(150); - } + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + codec->beep_just_power_on = true; + } else if (action == HDA_FIXUP_ACT_INIT) { +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* + * Just enable loopback to internal speaker and headphone jack. + * Disable amplification to get about the same beep volume as + * was on pure BIOS setup before loading the driver. + */ + alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13)); - codec->patch_ops.init(codec); + snd_hda_enable_beep_device(codec, 1); - if (spec->codec_variant == ALC269_TYPE_ALC269VB) - alc269vb_toggle_power_output(codec, 1); - if (spec->codec_variant == ALC269_TYPE_ALC269VB && - (alc_get_coef0(codec) & 0x00ff) == 0x017) { - msleep(200); +#if !IS_ENABLED(CONFIG_INPUT_PCSPKR) + dev_warn_once(hda_codec_dev(codec), + "enable CONFIG_INPUT_PCSPKR to get PC beeps\n"); +#endif +#endif } +} - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); +/* for hda_fixup_thinkpad_acpi() */ +#include "../helpers/thinkpad.c" - /* on some machine, the BIOS will clear the codec gpio data when enter - * suspend, and won't restore the data after resume, so we restore it - * in the driver. - */ - if (spec->gpio_data) - alc_write_gpio_data(codec); +static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */ + hda_fixup_thinkpad_acpi(codec, fix, action); +} - if (spec->has_alc5505_dsp) - alc5505_dsp_resume(codec); +/* for hda_fixup_ideapad_acpi() */ +#include "../helpers/ideapad_hotkey_led.c" - return 0; +static void alc_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + hda_fixup_ideapad_acpi(codec, fix, action); } -static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */ +static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.suppress_auto_mute = 1; + break; + } } -static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) { - unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21); - unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19); + struct hda_codec *cdc = data; + struct alc_spec *spec = cdc->spec; - if (cfg_headphone && cfg_headset_mic == 0x411111f0) - snd_hda_codec_set_pincfg(codec, 0x19, - (cfg_headphone & ~AC_DEFCFG_DEVICE) | - (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT)); + codec_info(cdc, "ACPI Notification %d\n", event); + + hda_component_acpi_device_notify(&spec->comps, handle, event, data); } -static void alc269_fixup_hweq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int comp_bind(struct device *dev) { - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x1e, 0, 0x80); + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + int ret; + + ret = hda_component_manager_bind(cdc, &spec->comps); + if (ret) + return ret; + + return hda_component_manager_bind_acpi_notifications(cdc, + &spec->comps, + comp_acpi_device_notify, cdc); } -static void alc269_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void comp_unbind(struct device *dev) { - struct alc_spec *spec = codec->spec; + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify); + hda_component_manager_unbind(cdc, &spec->comps); } -static void alc271_fixup_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static const struct component_master_ops comp_master_ops = { + .bind = comp_bind, + .unbind = comp_unbind, +}; + +static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) { - static const struct hda_verb verbs[] = { - {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, - {} - }; - unsigned int cfg; + struct alc_spec *spec = cdc->spec; - if (strcmp(codec->core.chip_name, "ALC271X") && - strcmp(codec->core.chip_name, "ALC269VB")) - return; - cfg = snd_hda_codec_get_pincfg(codec, 0x12); - if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) - snd_hda_sequence_write(codec, verbs); + hda_component_manager_playback_hook(&spec->comps, action); } -/* Fix the speaker amp after resume, etc */ -static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, const char *match_str, int count) { - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000); + struct alc_spec *spec = cdc->spec; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid, + match_str, &comp_master_ops); + if (ret) + return; + + spec->gen.pcm_playback_hook = comp_generic_playback_hook; + break; + case HDA_FIXUP_ACT_FREE: + hda_component_manager_free(&spec->comps, &comp_master_ops); + break; + } } -static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void find_cirrus_companion_amps(struct hda_codec *cdc) { - struct alc_spec *spec = codec->spec; + struct device *dev = hda_codec_dev(cdc); + struct acpi_device *adev; + struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; + const char *bus = NULL; + static const struct { + const char *hid; + const char *name; + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, + { "CSC3556", "cs35l56-hda" }, + { "CSC3557", "cs35l57-hda" }}; + char *match; + int i, count = 0, count_devindex = 0; - if (action != HDA_FIXUP_ACT_PROBE) + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); + if (adev) + break; + } + if (!adev) { + codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n"); return; + } - /* Due to a hardware problem on Lenovo Ideadpad, we need to - * fix the sample rate of analog I/O to 44.1kHz + count = i2c_acpi_client_count(adev); + if (count > 0) { + bus = "i2c"; + } else { + count = acpi_spi_count_resources(adev); + if (count > 0) + bus = "spi"; + } + + fwnode = fwnode_handle_get(acpi_fwnode_handle(adev)); + acpi_dev_put(adev); + + if (!bus) { + codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid); + return; + } + + if (!fwnode) { + codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid); + return; + } + + /* + * When available the cirrus,dev-index property is an accurate + * count of the amps in a system and is used in preference to + * the count of bus devices that can contain additional address + * alias entries. */ - spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); + if (count_devindex > 0) + count = count_devindex; + + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); + if (!match) + return; + codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); + comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); } -static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - /* The digital-mic unit sends PDM (differential signal) instead of - * the standard PCM, thus you can't record a valid mono stream as is. - * Below is a workaround specific to ALC269 to control the dmic - * signal source as mono. - */ - if (action == HDA_FIXUP_ACT_INIT) - alc_update_coef_idx(codec, 0x07, 0, 0x80); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } -static void alc269_quanta_automute(struct hda_codec *codec) +static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - snd_hda_gen_update_outputs(codec); + comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); +} - alc_write_coef_idx(codec, 0x0c, 0x680); - alc_write_coef_idx(codec, 0x0c, 0x480); +static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); } -static void alc269_fixup_quanta_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - if (action != HDA_FIXUP_ACT_PROBE) - return; - spec->gen.automute_hook = alc269_quanta_automute; + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1); } -static void alc269_x101_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) +static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - int vref; - msleep(200); - snd_hda_gen_hp_automute(codec, jack); + comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); +} - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - msleep(100); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); - msleep(500); - snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); +static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, + int action) +{ + comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); } -/* - * Magic sequence to make Huawei Matebook X right speaker working (bko#197801) - */ -struct hda_alc298_mbxinit { - unsigned char value_0x23; - unsigned char value_0x25; -}; +static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, + int action) +{ + comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); +} -static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec, - const struct hda_alc298_mbxinit *initval, - bool first) +static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0); - alc_write_coef_idx(codec, 0x26, 0xb000); + /* + * The same SSID has been re-used in different hardware, they have + * different codecs and the newer GA403U has a ALC285. + */ + if (cdc->core.vendor_id != 0x10ec0285) + alc_fixup_inv_dmic(cdc, fix, action); +} - if (first) - snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0); +static void tas2781_fixup_tias_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); +} - snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); - alc_write_coef_idx(codec, 0x26, 0xf000); - alc_write_coef_idx(codec, 0x23, initval->value_0x23); +static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); +} - if (initval->value_0x23 != 0x1e) - alc_write_coef_idx(codec, 0x25, initval->value_0x25); +static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1); +} - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); +static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); } -static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - /* Initialization magic */ - static const struct hda_alc298_mbxinit dac_init[] = { - {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, - {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00}, - {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00}, - {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24}, - {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f}, - {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00}, - {0x2f, 0x00}, - {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, - {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c}, - {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80}, - {} - }; - const struct hda_alc298_mbxinit *seq; + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); +} - if (action != HDA_FIXUP_ACT_INIT) - return; - /* Start */ - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00); - snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80); - alc_write_coef_idx(codec, 0x26, 0xf000); - alc_write_coef_idx(codec, 0x22, 0x31); - alc_write_coef_idx(codec, 0x23, 0x0b); - alc_write_coef_idx(codec, 0x25, 0x00); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26); - snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010); +/* for alc295_fixup_hp_top_speakers */ +#include "../helpers/hp_x360.c" - for (seq = dac_init; seq->value_0x23; seq++) - alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init); -} +/* for alc285_fixup_ideapad_s740_coef() */ +#include "../helpers/ideapad_s740.c" -static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { + WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), + WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), + WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089), + {} +}; + +static void alc256_fixup_set_coef_defaults(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook; - } + /* + * A certain other OS sets these coeffs to different values. On at least + * one TongFang barebone these settings might survive even a cold + * reboot. So to restore a clean slate the values are explicitly reset + * to default here. Without this, the external microphone is always in a + * plugged-in state, while the internal microphone is always in an + * unplugged state, breaking the ability to use the internal microphone. + */ + alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs); } -static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin, - bool polarity, bool on) -{ - unsigned int pinval; +static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = { + WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06), + WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074), + WRITE_COEF(0x49, 0x0149), + {} +}; - if (!pin) - return; - if (polarity) - on = !on; - pinval = snd_hda_codec_get_pin_target(codec, pin); - pinval &= ~AC_PINCTL_VREFEN; - pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ; - /* temporarily power up/down for setting VREF */ - snd_hda_power_up_pm(codec); - snd_hda_set_pin_ctl_cache(codec, pin, pinval); - snd_hda_power_down_pm(codec); +static void alc233_fixup_no_audio_jack(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + /* + * The audio jack input and output is not detected on the ASRock NUC Box + * 1100 series when cold booting without this fix. Warm rebooting from a + * certain other OS makes the audio functional, as COEF settings are + * preserved in this case. This fix sets these altered COEF values as + * the default. + */ + alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs); } -/* update mute-LED according to the speaker mute state via mic VREF pin */ -static int vref_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_vref_led(codec, spec->mute_led_nid, - spec->mute_led_polarity, brightness); - return 0; + /* + * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec, + * but uses the 0x8686 subproduct id in both cases. The ALC256 codec + * needs an additional quirk for sound working after suspend and resume. + */ + if (codec->core.vendor_id == 0x10ec0256) { + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120); + } else { + snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c); + } } -/* Make sure the led works even in runtime suspend */ -static unsigned int led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) +static void alc256_decrease_headphone_amp_val(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; + u32 caps; + u8 nsteps, offs; - if (power_state != AC_PWRST_D3 || nid == 0 || - (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid)) - return power_state; + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; - /* Set pin ctl again, it might have just been set to 0 */ - snd_hda_set_pin_ctl(codec, nid, - snd_hda_codec_get_pin_target(codec, nid)); + caps = query_amp_caps(codec, 0x3, HDA_OUTPUT); + nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10; + offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10; + caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET; + caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT); - return snd_hda_gen_path_power_filter(codec, nid, power_state); + if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps)) + codec_warn(codec, "failed to override amp caps for NID 0x3\n"); } -static void alc269_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { struct alc_spec *spec = codec->spec; - const struct dmi_device *dev = NULL; + struct hda_input_mux *imux = &spec->gen.input_mux; + int i; - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; + alc269_fixup_limit_int_mic_boost(codec, fix, action); - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - int pol, pin; - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) - continue; - if (pin < 0x0a || pin >= 0x10) - break; - spec->mute_led_polarity = pol; - spec->mute_led_nid = pin - 0x0a + 0x18; - snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); - codec->power_filter = led_power_filter; - codec_dbg(codec, - "Detected mute LED for %x:%d\n", spec->mute_led_nid, - spec->mute_led_polarity); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /** + * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic) + * to Hi-Z to avoid pop noises at startup and when plugging and + * unplugging headphones. + */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); + snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ); + break; + case HDA_FIXUP_ACT_PROBE: + /** + * Make the internal mic (0x12) the default input source to + * prevent pop noises on cold boot. + */ + for (i = 0; i < imux->num_items; i++) { + if (spec->gen.imux_pins[i] == 0x12) { + spec->gen.cur_mux[0] = i; + break; + } + } break; } } -static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec, - const struct hda_fixup *fix, - int action, hda_nid_t pin) +static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { + /* + * The Pin Complex 0x17 for the bass speakers is wrongly reported as + * unconnected. + */ + static const struct hda_pintbl pincfgs[] = { + { 0x17, 0x90170121 }, + { } + }; + /* + * Avoid DAC 0x06 and 0x08, as they have no volume controls. + * DAC 0x02 and 0x03 would be fine. + */ + static const hda_nid_t conn[] = { 0x02, 0x03 }; + /* + * Prefer both speakerbar (0x14) and bass speakers (0x17) connected to DAC 0x02. + * Headphones (0x21) are connected to DAC 0x03. + */ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x02, + 0x17, 0x02, + 0x21, 0x03, + 0 + }; struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_nid = pin; - snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set); - codec->power_filter = led_power_filter; + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; + break; } } -static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18); -} + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170151 }, + { 0x17, 0x90170150 }, + { } + }; + static const hda_nid_t conn[] = { 0x02, 0x03 }; + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x02, + 0x17, 0x03, + 0x21, 0x02, + 0 + }; + struct alc_spec *spec = codec->spec; -static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19); -} + alc_fixup_no_shutup(codec, fix, action); -static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; + break; + } } -/* update LED status via GPIO */ -static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, - int polarity, bool enabled) -{ - if (polarity) - enabled = !enabled; - alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ -} - -/* turn on/off mute LED via GPIO per vmaster hook */ -static int gpio_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mute_led_mask, - spec->mute_led_polarity, !brightness); - return 0; -} - -/* turn on/off mic-mute LED via GPIO per capture hook */ -static int micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_gpio_led(codec, spec->gpio_mic_led_mask, - spec->micmute_led_polarity, !brightness); - return 0; -} - -/* setup mute and mic-mute GPIO bits, add hooks appropriately */ -static void alc_fixup_hp_gpio_led(struct hda_codec *codec, - int action, - unsigned int mute_mask, - unsigned int micmute_mask) +/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */ +static void alc287_fixup_bind_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - - alc_fixup_gpio(codec, action, mute_mask | micmute_mask); + static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ + static const hda_nid_t preferred_pairs[] = { + 0x17, 0x02, 0x21, 0x03, 0 + }; if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (mute_mask) { - spec->gpio_mute_led_mask = mute_mask; - snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); - } - if (micmute_mask) { - spec->gpio_mic_led_mask = micmute_mask; - snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); - } -} - -static void alc236_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01); -} - -static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); -} - -static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01); -} - -static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); -} -static void alc287_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0x10, 0); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; + spec->gen.auto_mute_via_amp = 1; + if (spec->gen.autocfg.speaker_pins[0] != 0x14) { + snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + 0x0); /* Make sure 0x14 was disable */ + } } -static void alc245_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* Fix none verb table of Headset Mic pin */ +static void alc2xx_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x19, 0x03a1103c }, + { } + }; - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + break; + } } -/* turn on/off mic-mute LED per capture hook via VREF change */ -static int vref_micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_vref_led(codec, spec->cap_mute_led_nid, - spec->micmute_led_polarity, brightness); - return 0; -} + /* + * The Pin Complex 0x14 for the treble speakers is wrongly reported as + * unconnected. + * The Pin Complex 0x17 for the bass speakers has the lowest association + * and sequence values so shift it up a bit to squeeze 0x14 in. + */ + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, // top/treble + { 0x17, 0x90170111 }, // bottom/bass + { } + }; -static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; + /* + * Force DAC 0x02 for the bass speakers 0x17. + */ + static const hda_nid_t conn[] = { 0x02 }; - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* Like hp_gpio_mic1_led, but also needs GPIO4 low to - * enable headphone amp - */ - spec->gpio_mask |= 0x10; - spec->gpio_dir |= 0x10; - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; } -} - -static void alc280_fixup_hp_gpio4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; - } + cs35l41_fixup_i2c_two(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc245_fixup_hp_gpio_led(codec, fix, action); } -/* HP Spectre x360 14 model needs a unique workaround for enabling the amp; - * it needs to toggle the GPIO0 once on and off at each time (bko#210633) - */ -static void alc245_fixup_hp_x360_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* some changes for Spectre x360 16, 2024 model */ +static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { + /* + * The Pin Complex 0x14 for the treble speakers is wrongly reported as + * unconnected. + * The Pin Complex 0x17 for the bass speakers has the lowest association + * and sequence values so shift it up a bit to squeeze 0x14 in. + */ struct alc_spec *spec = codec->spec; + static const struct hda_pintbl pincfgs[] = { + { 0x14, 0x90170110 }, // top/treble + { 0x17, 0x90170111 }, // bottom/bass + { } + }; + + /* + * Force DAC 0x02 for the bass speakers 0x17. + */ + static const hda_nid_t conn[] = { 0x02 }; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: + /* needed for amp of back speakers */ spec->gpio_mask |= 0x01; spec->gpio_dir |= 0x01; + snd_hda_apply_pincfgs(codec, pincfgs); + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); break; case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp */ + /* need to toggle GPIO to enable the amp of back speakers */ alc_update_gpio_data(codec, 0x01, true); msleep(100); alc_update_gpio_data(codec, 0x01, false); break; } + + cs35l41_fixup_i2c_two(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc245_fixup_hp_gpio_led(codec, fix, action); } -/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */ -static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) +static void alc245_fixup_hp_zbook_firefly_g12a(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_GEN_PCM_ACT_PREPARE: - alc_update_gpio_data(codec, 0x04, true); - break; - case HDA_GEN_PCM_ACT_CLEANUP: - alc_update_gpio_data(codec, 0x04, false); + 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; } + + cs35l41_fixup_i2c_two(codec, fix, action); + alc245_fixup_hp_mute_led_coefbit(codec, fix, action); + alc285_fixup_hp_coef_micmute_led(codec, fix, action); } -static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) +/* + * ALC287 PCM hooks + */ +static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) { - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - spec->gpio_mask |= 0x04; - spec->gpio_dir |= 0x04; - spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook; + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ + break; + case HDA_GEN_PCM_ACT_CLOSE: + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + break; } } -static void alc_update_coef_led(struct hda_codec *codec, - struct alc_coef_led *led, - bool polarity, bool on) +static void alc287_s4_power_gpio3_default(struct hda_codec *codec) { - if (polarity) - on = !on; - /* temporarily power up/down for setting COEF bit */ - alc_update_coef_idx(codec, led->idx, led->mask, - on ? led->on : led->off); + if (is_s4_suspend(codec)) { + alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ + } } -/* update mute-LED according to the speaker mute state via COEF bit */ -static int coef_mute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); struct alc_spec *spec = codec->spec; + static const struct coef_fw coefs[] = { + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), + WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), + }; - alc_update_coef_led(codec, &spec->mute_led_coef, - spec->mute_led_polarity, brightness); - return 0; + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); + alc_process_coef_fw(codec, coefs); + spec->power_hook = alc287_s4_power_gpio3_default; + spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } -static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 1 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x34; - spec->mute_led_coef.mask = 1 << 5; - spec->mute_led_coef.on = 0; - spec->mute_led_coef.off = 1 << 5; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x07; - spec->mute_led_coef.mask = 1; - spec->mute_led_coef.on = 1; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 3 << 2; - spec->mute_led_coef.on = 2 << 2; - spec->mute_led_coef.off = 1 << 2; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0x0b; - spec->mute_led_coef.mask = 1 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 0; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -/* turn on/off mic-mute LED per capture hook by coef bit */ -static int coef_micmute_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); - struct alc_spec *spec = codec->spec; - - alc_update_coef_led(codec, &spec->mic_led_coef, - spec->micmute_led_polarity, brightness); - return 0; -} - -static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef.idx = 0x19; - spec->mic_led_coef.mask = 1 << 13; - spec->mic_led_coef.on = 1 << 13; - spec->mic_led_coef.off = 0; - snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); - } -} - -static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); -} - -static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mic_led_coef.idx = 0x35; - spec->mic_led_coef.mask = 3 << 2; - spec->mic_led_coef.on = 2 << 2; - spec->mic_led_coef.off = 1 << 2; - snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set); - } -} - -static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_coef.idx = 0xb; - spec->mute_led_coef.mask = 3 << 3; - spec->mute_led_coef.on = 1 << 3; - spec->mute_led_coef.off = 1 << 4; - snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); - } -} - -static void alc285_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_coef_micmute_led(codec, fix, action); -} - -static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_gpio_micmute_led(codec, fix, action); -} - -static void alc236_fixup_hp_mute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc236_fixup_hp_mute_led_coefbit(codec, fix, action); - alc236_fixup_hp_coef_micmute_led(codec, fix, action); -} - -static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x1a; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - codec->power_filter = led_power_filter; - } -} - -static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc236_fixup_hp_mute_led_coefbit(codec, fix, action); - alc236_fixup_hp_micmute_led_vref(codec, fix, action); -} - -static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec, - const unsigned short coefs[2]) -{ - alc_write_coef_idx(codec, 0x23, coefs[0]); - alc_write_coef_idx(codec, 0x25, coefs[1]); - alc_write_coef_idx(codec, 0x26, 0xb011); -} - -struct alc298_samsung_amp_desc { - unsigned char nid; - unsigned short init_seq[2][2]; -}; - -static void alc298_fixup_samsung_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* + * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly + * at PM resume + */ +static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - int i, j; - static const unsigned short init_seq[][2] = { - { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 }, - { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 }, - { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e }, - { 0x41, 0x07 }, { 0x400, 0x1 } - }; - static const struct alc298_samsung_amp_desc amps[] = { - { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } }, - { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } } - }; - - if (action != HDA_FIXUP_ACT_INIT) - return; - - for (i = 0; i < ARRAY_SIZE(amps); i++) { - alc_write_coef_idx(codec, 0x22, amps[i].nid); - - for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++) - alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]); - - for (j = 0; j < ARRAY_SIZE(init_seq); j++) - alc298_samsung_write_coef_pack(codec, init_seq[j]); - } + if (action == HDA_FIXUP_ACT_INIT) + alc_write_coef_idx(codec, 0xd, 0x2800); } -struct alc298_samsung_v2_amp_desc { - unsigned short nid; - int init_seq_size; - unsigned short init_seq[18][2]; -}; - -static const struct alc298_samsung_v2_amp_desc -alc298_samsung_v2_amp_desc_tbl[] = { - { 0x38, 18, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, - { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, - { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } - }}, - { 0x39, 18, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, - { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 }, - { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 } - }}, - { 0x3c, 15, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe }, - { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } - }}, - { 0x3d, 15, { - { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 }, - { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd }, - { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 }, - { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e }, - { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d } - }} -}; - -static void alc298_samsung_v2_enable_amps(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const unsigned short enable_seq[][2] = { - { 0x203a, 0x0081 }, { 0x23ff, 0x0001 }, - }; - int i, j; - - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < ARRAY_SIZE(enable_seq); j++) - alc298_samsung_write_coef_pack(codec, enable_seq[j]); - codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } -} - -static void alc298_samsung_v2_disable_amps(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const unsigned short disable_seq[][2] = { - { 0x23ff, 0x0000 }, { 0x203a, 0x0080 }, - }; - int i, j; - - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < ARRAY_SIZE(disable_seq); j++) - alc298_samsung_write_coef_pack(codec, disable_seq[j]); - codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } -} - -static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - /* Dynamically enable/disable speaker amps before and after playback */ - if (action == HDA_GEN_PCM_ACT_OPEN) - alc298_samsung_v2_enable_amps(codec); - if (action == HDA_GEN_PCM_ACT_CLOSE) - alc298_samsung_v2_disable_amps(codec); -} - -static void alc298_samsung_v2_init_amps(struct hda_codec *codec, - int num_speaker_amps) -{ - struct alc_spec *spec = codec->spec; - int i, j; - - /* Set spec's num_speaker_amps before doing anything else */ - spec->num_speaker_amps = num_speaker_amps; - - /* Disable speaker amps before init to prevent any physical damage */ - alc298_samsung_v2_disable_amps(codec); - - /* Initialize the speaker amps */ - for (i = 0; i < spec->num_speaker_amps; i++) { - alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid); - for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) { - alc298_samsung_write_coef_pack(codec, - alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]); - } - alc_write_coef_idx(codec, 0x89, 0x0); - codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n", - alc298_samsung_v2_amp_desc_tbl[i].nid); - } - - /* register hook to enable speaker amps only when they are needed */ - spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook; -} - -static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - alc298_samsung_v2_init_amps(codec, 2); -} - -static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PROBE) - alc298_samsung_v2_init_amps(codec, 4); -} - -static void gpio2_mic_hotkey_event(struct hda_codec *codec, - struct hda_jack_callback *event) -{ - struct alc_spec *spec = codec->spec; - - /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore - send both key on and key off event for every interrupt. */ - input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1); - input_sync(spec->kb_dev); - input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0); - input_sync(spec->kb_dev); -} - -static int alc_register_micmute_input_device(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - spec->kb_dev = input_allocate_device(); - if (!spec->kb_dev) { - codec_err(codec, "Out of memory (input_allocate_device)\n"); - return -ENOMEM; - } - - spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE; - - spec->kb_dev->name = "Microphone Mute Button"; - spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); - spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]); - spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map); - spec->kb_dev->keycode = spec->alc_mute_keycode_map; - for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++) - set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit); - - if (input_register_device(spec->kb_dev)) { - codec_err(codec, "input_register_device failed\n"); - input_free_device(spec->kb_dev); - spec->kb_dev = NULL; - return -ENOMEM; - } - - return 0; -} - -/* GPIO1 = set according to SKU external amp - * GPIO2 = mic mute hotkey - * GPIO3 = mute LED - * GPIO4 = mic mute LED - */ -static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->init_amp = ALC_INIT_DEFAULT; - if (alc_register_micmute_input_device(codec) != 0) - return; - - spec->gpio_mask |= 0x06; - spec->gpio_dir |= 0x02; - spec->gpio_data |= 0x02; - snd_hda_codec_write_cache(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); - snd_hda_jack_detect_enable_callback(codec, codec->core.afg, - gpio2_mic_hotkey_event); - return; - } - - if (!spec->kb_dev) - return; - - switch (action) { - case HDA_FIXUP_ACT_FREE: - input_unregister_device(spec->kb_dev); - spec->kb_dev = NULL; - } -} - -/* Line2 = mic mute hotkey - * GPIO2 = mic mute LED - */ -static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->init_amp = ALC_INIT_DEFAULT; - if (alc_register_micmute_input_device(codec) != 0) - return; - - snd_hda_jack_detect_enable_callback(codec, 0x1b, - gpio2_mic_hotkey_event); - return; - } - - if (!spec->kb_dev) - return; - - switch (action) { - case HDA_FIXUP_ACT_FREE: - input_unregister_device(spec->kb_dev); - spec->kb_dev = NULL; - } -} - -static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cap_mute_led_nid = 0x18; - snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set); - } -} - -static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->micmute_led_polarity = 1; - alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); -} - -static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(delay); -} - -static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) -{ - if (delay <= 0) - delay = 75; - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - msleep(delay); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - msleep(delay); -} - -static const struct coef_fw alc225_pre_hsmode[] = { - UPDATE_COEF(0x4a, 1<<8, 0), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), - UPDATE_COEF(0x63, 3<<14, 3<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x4a, 3<<10, 3<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), - UPDATE_COEF(0x4a, 3<<10, 0), - {} -}; - -static void alc_headset_mode_unplugged(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */ - WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ - WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ - WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */ - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x1b, 0x0c0b), - WRITE_COEF(0x45, 0xc429), - UPDATE_COEF(0x35, 0x4000, 0), - WRITE_COEF(0x06, 0x2104), - WRITE_COEF(0x1a, 0x0001), - WRITE_COEF(0x26, 0x0004), - WRITE_COEF(0x32, 0x42a3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x19, 0x1300, 0x0300), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x18, 0x7308), - WRITE_COEF(0x6b, 0xc429), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ - UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ - UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ - WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - {} - }; - static const struct coef_fw coef0668[] = { - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0100, 0), - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), - UPDATE_COEF(0x6b, 0xf000, 0x5000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x0c00, 0x0c00), - WRITE_COEF(0x45, 0x5289), - UPDATE_COEF(0x4a, 0x0c00, 0), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, coef0256); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0298: - alc_process_coef_fw(codec, coef0298); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0668); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_hp_mute_disable(codec, 75); - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to unplugged mode.\n"); -} - - -static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, - hda_nid_t mic_pin) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEFEX(0x57, 0x03, 0x8aa6), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0256[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEFEX(0x57, 0x03, 0x09a3), - WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ - {} - }; - static const struct coef_fw coef0233[] = { - UPDATE_COEF(0x35, 0, 1<<14), - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x1a, 0x0021), - WRITE_COEF(0x26, 0x008c), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0x00c0, 0), - UPDATE_COEF(0x50, 0x2000, 0), - UPDATE_COEF(0x56, 0x0006, 0), - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), - UPDATE_COEF(0x66, 0x0008, 0x0008), - UPDATE_COEF(0x67, 0x2000, 0x2000), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x19, 0xa208), - WRITE_COEF(0x2e, 0xacf0), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ - UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0xb5, 0x1040), - UPDATE_COEF(0xc3, 0, 1<<12), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), - UPDATE_COEF(0x4a, 3<<4, 2<<4), - UPDATE_COEF(0x63, 3<<14, 0), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x6b, 0xf000, 0), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0255); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x45, 0xc489); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0256); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0x4689); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0274); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0233); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0288); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0292: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - /* Set to TRS mode */ - alc_write_coef_idx(codec, 0x45, 0xc429); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0293); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); - fallthrough; - case 0x10ec0221: - case 0x10ec0662: - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0668: - alc_write_coef_idx(codec, 0x11, 0x0001); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0688); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); - snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); - alc_process_coef_fw(codec, coef0225); - snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); - break; - } - codec_dbg(codec, "Headset jack set to mic-in mode.\n"); -} - -static void alc_headset_mode_default(struct hda_codec *codec) -{ - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10), - UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10), - UPDATE_COEF(0x49, 3<<8, 0<<8), - UPDATE_COEF(0x4a, 3<<4, 3<<4), - UPDATE_COEF(0x63, 3<<14, 0), - UPDATE_COEF(0x67, 0xf000, 0x3000), - {} - }; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xc089), - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - WRITE_COEF(0x49, 0x0049), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xc489), - WRITE_COEFEX(0x57, 0x03, 0x0da3), - WRITE_COEF(0x49, 0x0049), - UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ - WRITE_COEF(0x06, 0x6100), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x06, 0x2100), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x76, 0x000e), - WRITE_COEF(0x6c, 0x2400), - WRITE_COEF(0x6b, 0xc429), - WRITE_COEF(0x18, 0x7308), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ - UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0041), - WRITE_COEF(0x15, 0x0d40), - WRITE_COEF(0xb7, 0x802b), - {} - }; - static const struct coef_fw coef0274[] = { - WRITE_COEF(0x45, 0x4289), - UPDATE_COEF(0x4a, 0x0010, 0x0010), - UPDATE_COEF(0x6b, 0x0f00, 0), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x45, 0xc089); - msleep(50); - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0286: - case 0x10ec0288: - case 0x10ec0298: - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); -} - -/* Iphone type */ -static void alc_headset_mode_ctia(struct hda_codec *codec) -{ - int val; - - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xd429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xd429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225_1[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - static const struct coef_fw coef0225_2[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), - UPDATE_COEF(0x63, 3<<14, 1<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xd689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - } - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) - alc_process_coef_fw(codec, coef0225_2); - else - alc_process_coef_fw(codec, coef0225_1); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0867: - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - } - codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); -} - -/* Nokia type */ -static void alc_headset_mode_omtp(struct hda_codec *codec) -{ - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEFEX(0x57, 0x03, 0x8ea6), - {} - }; - static const struct coef_fw coef0256[] = { - WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ - WRITE_COEF(0x1b, 0x0e6b), - {} - }; - static const struct coef_fw coef0233[] = { - WRITE_COEF(0x45, 0xe429), - WRITE_COEF(0x1b, 0x0c2b), - WRITE_COEF(0x32, 0x4ea3), - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - {} - }; - static const struct coef_fw coef0292[] = { - WRITE_COEF(0x6b, 0xe429), - WRITE_COEF(0x76, 0x0008), - WRITE_COEF(0x18, 0x7388), - {} - }; - static const struct coef_fw coef0293[] = { - WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ - UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x15, 0x0d50), - WRITE_COEF(0xc3, 0x0000), - {} - }; - static const struct coef_fw coef0225[] = { - UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), - UPDATE_COEF(0x63, 3<<14, 2<<14), - {} - }; - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, coef0256); - alc_hp_enable_unmute(codec, 75); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_write_coef_idx(codec, 0x45, 0xe689); - break; - case 0x10ec0233: - case 0x10ec0283: - alc_process_coef_fw(codec, coef0233); - break; - case 0x10ec0298: - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); - msleep(300); - alc_process_coef_fw(codec, coef0288); - break; - case 0x10ec0292: - alc_process_coef_fw(codec, coef0292); - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, coef0225); - alc_hp_enable_unmute(codec, 75); - break; - } - codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); -} - -static void alc_determine_headset_type(struct hda_codec *codec) -{ - int val; - bool is_ctia = false; - struct alc_spec *spec = codec->spec; - static const struct coef_fw coef0255[] = { - WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ - WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref - conteol) */ - {} - }; - static const struct coef_fw coef0288[] = { - UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ - {} - }; - static const struct coef_fw coef0298[] = { - UPDATE_COEF(0x50, 0x2000, 0x2000), - UPDATE_COEF(0x56, 0x0006, 0x0006), - UPDATE_COEF(0x66, 0x0008, 0), - UPDATE_COEF(0x67, 0x2000, 0), - UPDATE_COEF(0x19, 0x1300, 0x1300), - {} - }; - static const struct coef_fw coef0293[] = { - UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ - WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ - {} - }; - static const struct coef_fw coef0688[] = { - WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0xb7, 0x802b), - WRITE_COEF(0x15, 0x0d60), - WRITE_COEF(0xc3, 0x0c00), - {} - }; - static const struct coef_fw coef0274[] = { - UPDATE_COEF(0x4a, 0x0010, 0), - UPDATE_COEF(0x4a, 0x8000, 0), - WRITE_COEF(0x45, 0xd289), - UPDATE_COEF(0x49, 0x0300, 0x0300), - {} - }; - - if (spec->no_internal_mic_pin) { - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - return; - } - - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_write_coef_idx(codec, 0x1b, 0x0e4b); - alc_write_coef_idx(codec, 0x06, 0x6104); - alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); - - alc_process_coef_fw(codec, coef0255); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - if (!is_ctia) { - alc_write_coef_idx(codec, 0x45, 0xe089); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x0070) == 0x0070) - is_ctia = false; - else - is_ctia = true; - } - alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); - alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - alc_process_coef_fw(codec, coef0274); - msleep(850); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - break; - case 0x10ec0233: - case 0x10ec0283: - alc_write_coef_idx(codec, 0x45, 0xd029); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0298: - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(100); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); - msleep(200); - - val = alc_read_coef_idx(codec, 0x50); - if (val & (1 << 12)) { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } else { - alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - } - alc_process_coef_fw(codec, coef0298); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); - msleep(75); - snd_hda_codec_write(codec, 0x21, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - break; - case 0x10ec0286: - case 0x10ec0288: - alc_process_coef_fw(codec, coef0288); - msleep(350); - val = alc_read_coef_idx(codec, 0x50); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0292: - alc_write_coef_idx(codec, 0x6b, 0xd429); - msleep(300); - val = alc_read_coef_idx(codec, 0x6c); - is_ctia = (val & 0x001c) == 0x001c; - break; - case 0x10ec0293: - alc_process_coef_fw(codec, coef0293); - msleep(300); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x0070) == 0x0070; - break; - case 0x10ec0668: - alc_process_coef_fw(codec, coef0688); - msleep(300); - val = alc_read_coef_idx(codec, 0xbe); - is_ctia = (val & 0x1c02) == 0x1c02; - break; - case 0x10ec0215: - case 0x10ec0225: - case 0x10ec0285: - case 0x10ec0295: - case 0x10ec0289: - case 0x10ec0299: - alc_process_coef_fw(codec, alc225_pre_hsmode); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); - val = alc_read_coef_idx(codec, 0x45); - if (val & (1 << 9)) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } else { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(800); - val = alc_read_coef_idx(codec, 0x46); - is_ctia = (val & 0x00f0) == 0x00f0; - } - if (!is_ctia) { - alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); - alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); - msleep(100); - val = alc_read_coef_idx(codec, 0x46); - if ((val & 0x00f0) == 0x00f0) - is_ctia = false; - else - is_ctia = true; - } - alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); - alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); - alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); - break; - case 0x10ec0867: - is_ctia = true; - break; - } - - codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", - str_yes_no(is_ctia)); - spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; -} - -static void alc_update_headset_mode(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; - hda_nid_t hp_pin = alc_get_hp_pin(spec); - - int new_headset_mode; - - if (!snd_hda_jack_detect(codec, hp_pin)) - new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; - else if (mux_pin == spec->headset_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_HEADSET; - else if (mux_pin == spec->headphone_mic_pin) - new_headset_mode = ALC_HEADSET_MODE_MIC; - else - new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; - - if (new_headset_mode == spec->current_headset_mode) { - snd_hda_gen_update_outputs(codec); - return; - } - - switch (new_headset_mode) { - case ALC_HEADSET_MODE_UNPLUGGED: - alc_headset_mode_unplugged(codec); - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADSET: - if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) - alc_determine_headset_type(codec); - if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) - alc_headset_mode_ctia(codec); - else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) - alc_headset_mode_omtp(codec); - spec->gen.hp_jack_present = true; - break; - case ALC_HEADSET_MODE_MIC: - alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); - spec->gen.hp_jack_present = false; - break; - case ALC_HEADSET_MODE_HEADPHONE: - alc_headset_mode_default(codec); - spec->gen.hp_jack_present = true; - break; - } - if (new_headset_mode != ALC_HEADSET_MODE_MIC) { - snd_hda_set_pin_ctl_cache(codec, hp_pin, - AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); - if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin) - snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, - PIN_VREFHIZ); - } - spec->current_headset_mode = new_headset_mode; - - snd_hda_gen_update_outputs(codec); -} - -static void alc_update_headset_mode_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - alc_update_headset_mode(codec); -} - -static void alc_update_headset_jack_cb(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - snd_hda_gen_hp_automute(codec, jack); - alc_update_headset_mode(codec); -} - -static void alc_probe_headset_mode(struct hda_codec *codec) -{ - int i; - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - - /* Find mic pins */ - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) - spec->headset_mic_pin = cfg->inputs[i].pin; - if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) - spec->headphone_mic_pin = cfg->inputs[i].pin; - } - - WARN_ON(spec->gen.cap_sync_hook); - spec->gen.cap_sync_hook = alc_update_headset_mode_hook; - spec->gen.automute_hook = alc_update_headset_mode; - spec->gen.hp_automute_hook = alc_update_headset_jack_cb; -} - -static void alc_fixup_headset_mode(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; - break; - case HDA_FIXUP_ACT_PROBE: - alc_probe_headset_mode(codec); - break; - case HDA_FIXUP_ACT_INIT: - if (is_s3_resume(codec) || is_s4_resume(codec)) { - spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; - spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; - } - alc_update_headset_mode(codec); - break; - } -} - -static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - } - else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc255_set_default_jack_type(struct hda_codec *codec) -{ - /* Set to iphone type */ - static const struct coef_fw alc255fw[] = { - WRITE_COEF(0x1b, 0x880b), - WRITE_COEF(0x45, 0xd089), - WRITE_COEF(0x1b, 0x080b), - WRITE_COEF(0x46, 0x0004), - WRITE_COEF(0x1b, 0x0c0b), - {} - }; - static const struct coef_fw alc256fw[] = { - WRITE_COEF(0x1b, 0x884b), - WRITE_COEF(0x45, 0xd089), - WRITE_COEF(0x1b, 0x084b), - WRITE_COEF(0x46, 0x0004), - WRITE_COEF(0x1b, 0x0c4b), - {} - }; - switch (codec->core.vendor_id) { - case 0x10ec0255: - alc_process_coef_fw(codec, alc255fw); - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - alc_process_coef_fw(codec, alc256fw); - break; - } - msleep(30); -} - -static void alc_fixup_headset_mode_alc255(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - alc255_set_default_jack_type(codec); - } - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - alc255_set_default_jack_type(codec); - } - else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc288_update_headset_jack_cb(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - - alc_update_headset_jack_cb(codec, jack); - /* Headset Mic enable or disable, only for Dell Dino */ - alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present); -} - -static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_headset_mode(codec, fix, action); - if (action == HDA_FIXUP_ACT_PROBE) { - struct alc_spec *spec = codec->spec; - /* toggled via hp_automute_hook */ - spec->gpio_mask |= 0x40; - spec->gpio_dir |= 0x40; - spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; - } -} - -static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->gen.auto_mute_via_amp = 1; - } -} - -static void alc_fixup_no_shutup(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - spec->no_shutup_pins = 1; - } -} - -static void alc_fixup_disable_aamix(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - struct alc_spec *spec = codec->spec; - /* Disable AA-loopback as it causes white noise */ - spec->gen.mixer_nid = 0; - } -} - -/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ -static void alc_fixup_tpt440_dock(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x16, 0x21211010 }, /* dock headphone */ - { 0x19, 0x21a11010 }, /* dock mic */ - { } - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - codec->power_save_node = 0; /* avoid click noises */ - snd_hda_apply_pincfgs(codec, pincfgs); - } -} - -static void alc_fixup_tpt470_dock(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x17, 0x21211010 }, /* dock headphone */ - { 0x19, 0x21a11010 }, /* dock mic */ - { } - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - snd_hda_apply_pincfgs(codec, pincfgs); - } else if (action == HDA_FIXUP_ACT_INIT) { - /* Enable DOCK device */ - snd_hda_codec_write(codec, 0x17, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); - /* Enable DOCK device */ - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0); - } -} - -static void alc_fixup_tpt470_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise - * the speaker output becomes too low by some reason on Thinkpads with - * ALC298 codec - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, - 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -static void alc295_fixup_asus_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t preferred_pairs[] = { - 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -static void alc_shutup_dell_xps13(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int hp_pin = alc_get_hp_pin(spec); - - /* Prevent pop noises when headphones are plugged in */ - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); - msleep(20); -} - -static void alc_fixup_dell_xps13(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - int i; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise - * it causes a click noise at start up - */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); - spec->shutup = alc_shutup_dell_xps13; - break; - case HDA_FIXUP_ACT_PROBE: - /* Make the internal mic the default input source. */ - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == 0x12) { - spec->gen.cur_mux[0] = i; - break; - } - } - break; - } -} - -static void alc_fixup_headset_mode_alc662(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */ - - /* Disable boost for mic-in permanently. (This code is only called - from quirks that guarantee that the headphone is at NID 0x1b.) */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000); - snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP); - } else - alc_fixup_headset_mode(codec, fix, action); -} - -static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - alc_write_coef_idx(codec, 0xc4, 0x8000); - alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } - alc_fixup_headset_mode(codec, fix, action); -} - -/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ -static int find_ext_mic_pin(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t nid; - unsigned int defcfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - nid = cfg->inputs[i].pin; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) - continue; - return nid; - } - - return 0; -} - -static void alc271_hp_gate_mic_jack(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PROBE) { - int mic_pin = find_ext_mic_pin(codec); - int hp_pin = alc_get_hp_pin(spec); - - if (snd_BUG_ON(!mic_pin || !hp_pin)) - return; - snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin); - } -} - -static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int i; - - /* The mic boosts on level 2 and 3 are too noisy - on the internal mic input. - Therefore limit the boost to 0 or 1. */ - - if (action != HDA_FIXUP_ACT_PROBE) - return; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int defcfg; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - continue; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - continue; - - snd_hda_override_amp_caps(codec, nid, HDA_INPUT, - (0x00 << AC_AMPCAP_OFFSET_SHIFT) | - (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - } -} - -static void alc283_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - - msleep(600); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -static void alc283_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_wcaps(codec, 0x03, 0); - /* Disable AA-loopback as it causes white noise */ - spec->gen.mixer_nid = 0; - break; - case HDA_FIXUP_ACT_INIT: - /* MIC2-VREF control */ - /* Set to manual mode */ - alc_update_coef_idx(codec, 0x06, 0x000c, 0); - /* Enable Line1 input control by verb */ - alc_update_coef_idx(codec, 0x1a, 0, 1 << 4); - break; - } -} - -static void alc283_fixup_sense_combo_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.hp_automute_hook = alc283_hp_automute_hook; - break; - case HDA_FIXUP_ACT_INIT: - /* MIC2-VREF control */ - /* Set to manual mode */ - alc_update_coef_idx(codec, 0x06, 0x000c, 0); - break; - } -} - -/* mute tablet speaker pin (0x14) via dock plugging in addition */ -static void asus_tx300_automute(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - snd_hda_gen_update_outputs(codec); - if (snd_hda_jack_detect(codec, 0x1b)) - spec->gen.mute_bits |= (1ULL << 0x14); -} - -static void alc282_fixup_asus_tx300(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl dock_pins[] = { - { 0x1b, 0x21114000 }, /* dock speaker pin */ - {} - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - /* TX300 needs to set up GPIO2 for the speaker amp */ - alc_setup_gpio(codec, 0x04); - snd_hda_apply_pincfgs(codec, dock_pins); - spec->gen.auto_mute_via_amp = 1; - spec->gen.automute_hook = asus_tx300_automute; - snd_hda_jack_detect_enable_callback(codec, 0x1b, - snd_hda_gen_hp_automute); - break; - case HDA_FIXUP_ACT_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - break; - case HDA_FIXUP_ACT_BUILD: - /* this is a bit tricky; give more sane names for the main - * (tablet) speaker and the dock speaker, respectively - */ - rename_ctl(codec, "Speaker Playback Switch", - "Dock Speaker Playback Switch"); - rename_ctl(codec, "Bass Speaker Playback Switch", - "Speaker Playback Switch"); - break; - } -} - -static void alc290_fixup_mono_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* DAC node 0x03 is giving mono output. We therefore want to - make sure 0x14 (front speaker) and 0x15 (headphones) use the - stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */ - static const hda_nid_t conn1[] = { 0x0c }; - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); - } -} - -static void alc298_fixup_speaker_volume(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* The speaker is routed to the Node 0x06 by a mistake, as a result - we can't adjust the speaker's volume since this node does not has - Amp-out capability. we change the speaker's route to: - Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 ( - Pin Complex), since Node 0x02 has Amp-out caps, we can adjust - speaker's volume now. */ - - static const hda_nid_t conn1[] = { 0x0c }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1); - } -} - -/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */ -static void alc295_fixup_disable_dac3(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02, 0x03 }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - } -} - -/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */ -static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02 }; - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - } -} - -/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */ -static void alc294_fixup_bass_speaker_15(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - static const hda_nid_t conn[] = { 0x02, 0x03 }; - snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); - snd_hda_gen_add_micmute_led_cdev(codec, NULL); - } -} - -/* Hook to update amp GPIO4 for automute */ -static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_gen_hp_automute(codec, jack); - /* mute_led_polarity is set to 0, so we pass inverted value here */ - alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity, - !spec->gen.hp_jack_present); -} - -/* Manage GPIOs for HP EliteBook Folio 9480m. - * - * GPIO4 is the headphone amplifier power control - * GPIO3 is the audio output mute indicator LED - */ - -static void alc280_fixup_hp_9480m(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x08, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */ - spec->gpio_mask |= 0x10; - spec->gpio_dir |= 0x10; - spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook; - } -} - -static void alc275_fixup_gpio4_off(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask |= 0x04; - spec->gpio_dir |= 0x04; - /* set data bit low */ - } -} - -/* Quirk for Thinkpad X1 7th and 8th Gen - * The following fixed routing needed - * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly - * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC - * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp - */ -static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - case HDA_FIXUP_ACT_BUILD: - /* The generic parser creates somewhat unintuitive volume ctls - * with the fixed routing above, and the shared DAC2 may be - * confusing for PA. - * Rename those to unique names so that PA doesn't touch them - * and use only Master volume. - */ - rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume"); - rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume"); - break; - } -} - -static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - alc_fixup_dual_codecs(codec, fix, action); - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); - break; - case HDA_FIXUP_ACT_BUILD: - /* rename Capture controls depending on the codec */ - rename_ctl(codec, "Capture Volume", - codec->addr == 0 ? - "Rear-Panel Capture Volume" : - "Front-Panel Capture Volume"); - rename_ctl(codec, "Capture Switch", - codec->addr == 0 ? - "Rear-Panel Capture Switch" : - "Front-Panel Capture Switch"); - break; - } -} - -static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - codec->power_save_node = 1; -} - -/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ -static void alc274_fixup_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t preferred_pairs[] = { - 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02, - 0 - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - spec->gen.preferred_dacs = preferred_pairs; - spec->gen.auto_mute_via_amp = 1; - codec->power_save_node = 0; -} - -/* avoid DAC 0x06 for speaker switch 0x17; it has no volume control */ -static void alc274_fixup_hp_aio_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - /* The speaker is routed to the Node 0x06 by a mistake, thus the - * speaker's volume can't be adjusted since the node doesn't have - * Amp-out capability. Assure the speaker and lineout pin to be - * coupled with DAC NID 0x02. - */ - static const hda_nid_t preferred_pairs[] = { - 0x16, 0x02, 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; -} - -/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */ -static void alc289_fixup_asus_ga401(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0 - }; - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gen.preferred_dacs = preferred_pairs; -} - -/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */ -static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_wcaps(codec, 0x03, 0); -} - -static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec) -{ - switch (codec->core.vendor_id) { - case 0x10ec0274: - case 0x10ec0294: - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */ - alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15); - break; - case 0x10ec0230: - case 0x10ec0235: - case 0x10ec0236: - case 0x10ec0255: - case 0x10ec0256: - case 0x10ec0257: - case 0x19e58326: - alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */ - alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15); - break; - } -} - -static void alc295_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->ultra_low_power = true; - break; - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -static void alc256_fixup_chromebook(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - if (codec->core.subsystem_id == 0x10280d76) - spec->gen.suppress_auto_mute = 0; - else - spec->gen.suppress_auto_mute = 1; - spec->gen.suppress_auto_mic = 1; - spec->en_3kpull_low = false; - break; - } -} - -static void alc_fixup_disable_mic_vref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); -} - - -static void alc294_gx502_toggle_output(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* The Windows driver sets the codec up in a very different way where - * it appears to leave 0x10 = 0x8a20 set. For Linux we need to toggle it - */ - if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) - alc_write_coef_idx(codec, 0x10, 0x8a20); - else - alc_write_coef_idx(codec, 0x10, 0x0a20); -} - -static void alc294_fixup_gx502_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Pin 0x21: headphones/headset mic */ - if (!is_jack_detectable(codec, 0x21)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc294_gx502_toggle_output); - break; - case HDA_FIXUP_ACT_INIT: - /* Make sure to start in a correct state, i.e. if - * headphones have been plugged in before powering up the system - */ - alc294_gx502_toggle_output(codec, NULL); - break; - } -} - -static void alc294_gu502_toggle_output(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* Windows sets 0x10 to 0x8420 for Node 0x20 which is - * responsible from changes between speakers and headphones - */ - if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT) - alc_write_coef_idx(codec, 0x10, 0x8420); - else - alc_write_coef_idx(codec, 0x10, 0x0a20); -} - -static void alc294_fixup_gu502_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (!is_jack_detectable(codec, 0x21)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x21, - alc294_gu502_toggle_output); - break; - case HDA_FIXUP_ACT_INIT: - alc294_gu502_toggle_output(codec, NULL); - break; - } -} - -static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_INIT) - return; - - msleep(100); - alc_write_coef_idx(codec, 0x65, 0x0); -} - -static void alc274_fixup_hp_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - switch (action) { - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -static void alc_fixup_no_int_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* Mic RING SLEEVE swap for combo jack */ - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - spec->no_internal_mic_pin = true; - break; - case HDA_FIXUP_ACT_INIT: - alc_combo_jack_hp_jd_restart(codec); - break; - } -} - -/* GPIO1 = amplifier on/off - * GPIO3 = mic mute LED - */ -static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02 }; - - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* front/high speakers */ - { 0x17, 0x90170130 }, /* back/bass speakers */ - { } - }; - - //enable micmute led - alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->micmute_led_polarity = 1; - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - /* share DAC to have unified volume control */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } -} - -/* GPIO1 = amplifier on/off */ -static void alc285_fixup_hp_spectre_x360_df1(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t conn[] = { 0x02 }; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* front/high speakers */ - { 0x17, 0x90170130 }, /* back/bass speakers */ - { } - }; - - // enable mute led - alc285_fixup_hp_mute_led_coefbit(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - /* share DAC to have unified volume control */ - snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } -} - -static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const hda_nid_t conn[] = { 0x02 }; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, /* rear speaker */ - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - /* force front speaker to DAC1 */ - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - } -} - -static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - static const struct coef_fw coefs[] = { - WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023), - WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03), - WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a), - WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014), - WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15), - WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489), - WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0), - WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000), - WRITE_COEF(0x6e, 0x1005), { } - }; - - static const struct hda_pintbl pincfgs[] = { - { 0x12, 0xb7a60130 }, /* Internal microphone*/ - { 0x14, 0x90170150 }, /* B&O soundbar speakers */ - { 0x17, 0x90170153 }, /* Side speakers */ - { 0x19, 0x03a11040 }, /* Headset microphone */ - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - - /* Fixes volume control problem for side speakers */ - alc295_fixup_disable_dac3(codec, fix, action); - - /* Fixes no sound from headset speaker */ - snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0); - - /* Auto-enable headset mic when plugged */ - snd_hda_jack_set_gating_jack(codec, 0x19, 0x21); - - /* Headset mic volume enhancement */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50); - break; - case HDA_FIXUP_ACT_INIT: - alc_process_coef_fw(codec, coefs); - break; - case HDA_FIXUP_ACT_BUILD: - rename_ctl(codec, "Bass Speaker Playback Volume", - "B&O-Tuned Playback Volume"); - rename_ctl(codec, "Front Playback Switch", - "B&O Soundbar Playback Switch"); - rename_ctl(codec, "Bass Speaker Playback Switch", - "Side Speaker Playback Switch"); - break; - } -} - -static void alc285_fixup_hp_beep(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - codec->beep_just_power_on = true; - } else if (action == HDA_FIXUP_ACT_INIT) { -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* - * Just enable loopback to internal speaker and headphone jack. - * Disable amplification to get about the same beep volume as - * was on pure BIOS setup before loading the driver. - */ - alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13)); - - snd_hda_enable_beep_device(codec, 1); - -#if !IS_ENABLED(CONFIG_INPUT_PCSPKR) - dev_warn_once(hda_codec_dev(codec), - "enable CONFIG_INPUT_PCSPKR to get PC beeps\n"); -#endif -#endif - } -} - -/* for hda_fixup_thinkpad_acpi() */ -#include "helpers/thinkpad.c" - -static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */ - hda_fixup_thinkpad_acpi(codec, fix, action); -} - -/* for hda_fixup_ideapad_acpi() */ -#include "helpers/ideapad_hotkey_led.c" - -static void alc_fixup_ideapad_acpi(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - hda_fixup_ideapad_acpi(codec, fix, action); -} - -/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */ -static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.suppress_auto_mute = 1; - break; - } -} - -static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data) -{ - struct hda_codec *cdc = data; - struct alc_spec *spec = cdc->spec; - - codec_info(cdc, "ACPI Notification %d\n", event); - - hda_component_acpi_device_notify(&spec->comps, handle, event, data); -} - -static int comp_bind(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - int ret; - - ret = hda_component_manager_bind(cdc, &spec->comps); - if (ret) - return ret; - - return hda_component_manager_bind_acpi_notifications(cdc, - &spec->comps, - comp_acpi_device_notify, cdc); -} - -static void comp_unbind(struct device *dev) -{ - struct hda_codec *cdc = dev_to_hda_codec(dev); - struct alc_spec *spec = cdc->spec; - - hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify); - hda_component_manager_unbind(cdc, &spec->comps); -} - -static const struct component_master_ops comp_master_ops = { - .bind = comp_bind, - .unbind = comp_unbind, -}; - -static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, - struct snd_pcm_substream *sub, int action) -{ - struct alc_spec *spec = cdc->spec; - - hda_component_manager_playback_hook(&spec->comps, action); -} - -static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus, - const char *hid, const char *match_str, int count) -{ - struct alc_spec *spec = cdc->spec; - int ret; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid, - match_str, &comp_master_ops); - if (ret) - return; - - spec->gen.pcm_playback_hook = comp_generic_playback_hook; - break; - case HDA_FIXUP_ACT_FREE: - hda_component_manager_free(&spec->comps, &comp_master_ops); - break; - } -} - -static void find_cirrus_companion_amps(struct hda_codec *cdc) -{ - struct device *dev = hda_codec_dev(cdc); - struct acpi_device *adev; - struct fwnode_handle *fwnode __free(fwnode_handle) = NULL; - const char *bus = NULL; - static const struct { - const char *hid; - const char *name; - } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, - { "CSC3556", "cs35l56-hda" }, - { "CSC3557", "cs35l57-hda" }}; - char *match; - int i, count = 0, count_devindex = 0; - - for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { - adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); - if (adev) - break; - } - if (!adev) { - codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n"); - return; - } - - count = i2c_acpi_client_count(adev); - if (count > 0) { - bus = "i2c"; - } else { - count = acpi_spi_count_resources(adev); - if (count > 0) - bus = "spi"; - } - - fwnode = fwnode_handle_get(acpi_fwnode_handle(adev)); - acpi_dev_put(adev); - - if (!bus) { - codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid); - return; - } - - if (!fwnode) { - codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid); - return; - } - - /* - * When available the cirrus,dev-index property is an accurate - * count of the amps in a system and is used in preference to - * the count of bus devices that can contain additional address - * alias entries. - */ - count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); - if (count_devindex > 0) - count = count_devindex; - - match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); - if (!match) - return; - codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); - comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); -} - -static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); -} - -static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); -} - -static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); -} - -static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1); -} - -static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4); -} - -static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, - int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2); -} - -static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, - int action) -{ - comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2); -} - -static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - /* - * The same SSID has been re-used in different hardware, they have - * different codecs and the newer GA403U has a ALC285. - */ - if (cdc->core.vendor_id != 0x10ec0285) - alc_fixup_inv_dmic(cdc, fix, action); -} - -static void tas2781_fixup_tias_i2c(struct hda_codec *cdc, - const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); -} - -static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); -} - -static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc, - const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1); -} - -static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, - const struct hda_fixup *fix, int action) -{ - comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1); -} - -static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - alc_fixup_hp_gpio_led(codec, action, 0, 0x04); -} - - -/* for alc295_fixup_hp_top_speakers */ -#include "helpers/hp_x360.c" - -/* for alc285_fixup_ideapad_s740_coef() */ -#include "helpers/ideapad_s740.c" - -static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { - WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), - WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), - WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089), - {} -}; - -static void alc256_fixup_set_coef_defaults(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * A certain other OS sets these coeffs to different values. On at least - * one TongFang barebone these settings might survive even a cold - * reboot. So to restore a clean slate the values are explicitly reset - * to default here. Without this, the external microphone is always in a - * plugged-in state, while the internal microphone is always in an - * unplugged state, breaking the ability to use the internal microphone. - */ - alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs); -} - -static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = { - WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06), - WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074), - WRITE_COEF(0x49, 0x0149), - {} -}; - -static void alc233_fixup_no_audio_jack(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * The audio jack input and output is not detected on the ASRock NUC Box - * 1100 series when cold booting without this fix. Warm rebooting from a - * certain other OS makes the audio functional, as COEF settings are - * preserved in this case. This fix sets these altered COEF values as - * the default. - */ - alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs); -} - -static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - /* - * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec, - * but uses the 0x8686 subproduct id in both cases. The ALC256 codec - * needs an additional quirk for sound working after suspend and resume. - */ - if (codec->core.vendor_id == 0x10ec0256) { - alc_update_coef_idx(codec, 0x10, 1<<9, 0); - snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120); - } else { - snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c); - } -} - -static void alc256_decrease_headphone_amp_val(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - u32 caps; - u8 nsteps, offs; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - caps = query_amp_caps(codec, 0x3, HDA_OUTPUT); - nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10; - offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10; - caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET; - caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT); - - if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps)) - codec_warn(codec, "failed to override amp caps for NID 0x3\n"); -} - -static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->gen.input_mux; - int i; - - alc269_fixup_limit_int_mic_boost(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /** - * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic) - * to Hi-Z to avoid pop noises at startup and when plugging and - * unplugging headphones. - */ - snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); - snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ); - break; - case HDA_FIXUP_ACT_PROBE: - /** - * Make the internal mic (0x12) the default input source to - * prevent pop noises on cold boot. - */ - for (i = 0; i < imux->num_items; i++) { - if (spec->gen.imux_pins[i] == 0x12) { - spec->gen.cur_mux[0] = i; - break; - } - } - break; - } -} - -static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x17 for the bass speakers is wrongly reported as - * unconnected. - */ - static const struct hda_pintbl pincfgs[] = { - { 0x17, 0x90170121 }, - { } - }; - /* - * Avoid DAC 0x06 and 0x08, as they have no volume controls. - * DAC 0x02 and 0x03 would be fine. - */ - static const hda_nid_t conn[] = { 0x02, 0x03 }; - /* - * Prefer both speakerbar (0x14) and bass speakers (0x17) connected to DAC 0x02. - * Headphones (0x21) are connected to DAC 0x03. - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, - 0x17, 0x02, - 0x21, 0x03, - 0 - }; - struct alc_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - } -} - -static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170151 }, - { 0x17, 0x90170150 }, - { } - }; - static const hda_nid_t conn[] = { 0x02, 0x03 }; - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x02, - 0x17, 0x03, - 0x21, 0x02, - 0 - }; - struct alc_spec *spec = codec->spec; - - alc_fixup_no_shutup(codec, fix, action); - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - break; - } -} - -/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */ -static void alc287_fixup_bind_dacs(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ - static const hda_nid_t preferred_pairs[] = { - 0x17, 0x02, 0x21, 0x03, 0 - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - spec->gen.preferred_dacs = preferred_pairs; - spec->gen.auto_mute_via_amp = 1; - if (spec->gen.autocfg.speaker_pins[0] != 0x14) { - snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - 0x0); /* Make sure 0x14 was disable */ - } -} -/* Fix none verb table of Headset Mic pin */ -static void alc_fixup_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x19, 0x03a1103c }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - break; - } -} - -static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x14 for the treble speakers is wrongly reported as - * unconnected. - * The Pin Complex 0x17 for the bass speakers has the lowest association - * and sequence values so shift it up a bit to squeeze 0x14 in. - */ - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, // top/treble - { 0x17, 0x90170111 }, // bottom/bass - { } - }; - - /* - * Force DAC 0x02 for the bass speakers 0x17. - */ - static const hda_nid_t conn[] = { 0x02 }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc245_fixup_hp_gpio_led(codec, fix, action); -} - -/* some changes for Spectre x360 16, 2024 model */ -static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* - * The Pin Complex 0x14 for the treble speakers is wrongly reported as - * unconnected. - * The Pin Complex 0x17 for the bass speakers has the lowest association - * and sequence values so shift it up a bit to squeeze 0x14 in. - */ - struct alc_spec *spec = codec->spec; - static const struct hda_pintbl pincfgs[] = { - { 0x14, 0x90170110 }, // top/treble - { 0x17, 0x90170111 }, // bottom/bass - { } - }; - - /* - * Force DAC 0x02 for the bass speakers 0x17. - */ - static const hda_nid_t conn[] = { 0x02 }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - /* needed for amp of back speakers */ - spec->gpio_mask |= 0x01; - spec->gpio_dir |= 0x01; - snd_hda_apply_pincfgs(codec, pincfgs); - snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); - break; - case HDA_FIXUP_ACT_INIT: - /* need to toggle GPIO to enable the amp of back speakers */ - alc_update_gpio_data(codec, 0x01, true); - msleep(100); - alc_update_gpio_data(codec, 0x01, false); - break; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc245_fixup_hp_gpio_led(codec, fix, action); -} - -static void alc245_fixup_hp_zbook_firefly_g12a(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; - } - - cs35l41_fixup_i2c_two(codec, fix, action); - alc245_fixup_hp_mute_led_coefbit(codec, fix, action); - alc285_fixup_hp_coef_micmute_led(codec, fix, action); -} - -/* - * ALC287 PCM hooks - */ -static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) -{ - switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */ - break; - case HDA_GEN_PCM_ACT_CLOSE: - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ - break; - } -} - -static void alc287_s4_power_gpio3_default(struct hda_codec *codec) -{ - if (is_s4_suspend(codec)) { - alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */ - } -} - -static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - static const struct coef_fw coefs[] = { - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300), - WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), - WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301), - WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023), - }; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11); - alc_process_coef_fw(codec, coefs); - spec->power_hook = alc287_s4_power_gpio3_default; - spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; -} - -/* - * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly - * at PM resume - */ -static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_INIT) - alc_write_coef_idx(codec, 0xd, 0x2800); -} - -enum { - ALC269_FIXUP_GPIO2, - ALC269_FIXUP_SONY_VAIO, - ALC275_FIXUP_SONY_VAIO_GPIO2, - ALC269_FIXUP_DELL_M101Z, - ALC269_FIXUP_SKU_IGNORE, - ALC269_FIXUP_ASUS_G73JW, - ALC269_FIXUP_ASUS_N7601ZM_PINS, - ALC269_FIXUP_ASUS_N7601ZM, - ALC269_FIXUP_LENOVO_EAPD, - ALC275_FIXUP_SONY_HWEQ, - ALC275_FIXUP_SONY_DISABLE_AAMIX, - ALC271_FIXUP_DMIC, - ALC269_FIXUP_PCM_44K, - ALC269_FIXUP_STEREO_DMIC, - ALC269_FIXUP_HEADSET_MIC, - ALC269_FIXUP_QUANTA_MUTE, - ALC269_FIXUP_LIFEBOOK, - ALC269_FIXUP_LIFEBOOK_EXTMIC, - ALC269_FIXUP_LIFEBOOK_HP_PIN, - ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT, - ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, - ALC269_FIXUP_AMIC, - ALC269_FIXUP_DMIC, - ALC269VB_FIXUP_AMIC, - ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_HP_MUTE_LED, - ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC269_FIXUP_HP_MUTE_LED_MIC2, - ALC269_FIXUP_HP_MUTE_LED_MIC3, - ALC269_FIXUP_HP_GPIO_LED, - ALC269_FIXUP_HP_GPIO_MIC1_LED, - ALC269_FIXUP_HP_LINE1_MIC1_LED, - ALC269_FIXUP_INV_DMIC, - ALC269_FIXUP_LENOVO_DOCK, - ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, - ALC269_FIXUP_NO_SHUTUP, - ALC286_FIXUP_SONY_MIC_NO_PRESENCE, - ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, - ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, - ALC269_FIXUP_HEADSET_MODE, - ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, - ALC269_FIXUP_ASPIRE_HEADSET_MIC, - ALC269_FIXUP_ASUS_X101_FUNC, - ALC269_FIXUP_ASUS_X101_VERB, - ALC269_FIXUP_ASUS_X101, - ALC271_FIXUP_AMIC_MIC2, - ALC271_FIXUP_HP_GATE_MIC_JACK, - ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, - ALC269_FIXUP_ACER_AC700, - ALC269_FIXUP_LIMIT_INT_MIC_BOOST, - ALC269VB_FIXUP_ASUS_ZENBOOK, - ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, - ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, - ALC269VB_FIXUP_ORDISSIMO_EVE2, - ALC283_FIXUP_CHROME_BOOK, - ALC283_FIXUP_SENSE_COMBO_JACK, - ALC282_FIXUP_ASUS_TX300, - ALC283_FIXUP_INT_MIC, - ALC290_FIXUP_MONO_SPEAKERS, - ALC290_FIXUP_MONO_SPEAKERS_HSJACK, - ALC290_FIXUP_SUBWOOFER, - ALC290_FIXUP_SUBWOOFER_HSJACK, - ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, - ALC269_FIXUP_THINKPAD_ACPI, - ALC269_FIXUP_LENOVO_XPAD_ACPI, - ALC269_FIXUP_DMIC_THINKPAD_ACPI, - ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, - ALC269VC_FIXUP_INFINIX_Y4_MAX, - ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, - ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC255_FIXUP_HEADSET_MODE, - ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, - ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_FIXUP_TPT440_DOCK, - ALC292_FIXUP_TPT440, - ALC283_FIXUP_HEADSET_MIC, - ALC255_FIXUP_MIC_MUTE_LED, - ALC282_FIXUP_ASPIRE_V5_PINS, - ALC269VB_FIXUP_ASPIRE_E1_COEF, - ALC280_FIXUP_HP_GPIO4, - ALC286_FIXUP_HP_GPIO_LED, - ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, - ALC280_FIXUP_HP_DOCK_PINS, - ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, - ALC280_FIXUP_HP_9480M, - ALC245_FIXUP_HP_X360_AMP, - ALC285_FIXUP_HP_SPECTRE_X360_EB1, - ALC285_FIXUP_HP_SPECTRE_X360_DF1, - ALC285_FIXUP_HP_ENVY_X360, - ALC288_FIXUP_DELL_HEADSET_MODE, - ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC288_FIXUP_DELL_XPS_13, - ALC288_FIXUP_DISABLE_AAMIX, - ALC292_FIXUP_DELL_E7X_AAMIX, - ALC292_FIXUP_DELL_E7X, - ALC292_FIXUP_DISABLE_AAMIX, - ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, - ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, - ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, - ALC275_FIXUP_DELL_XPS, - ALC293_FIXUP_LENOVO_SPK_NOISE, - ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, - ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, - ALC255_FIXUP_DELL_SPK_NOISE, - ALC225_FIXUP_DISABLE_MIC_VREF, - ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC295_FIXUP_DISABLE_DAC3, - ALC285_FIXUP_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_HEADSET_MIC, - ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, - ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_I2C_HEADSET_MIC, - ALC280_FIXUP_HP_HEADSET_MIC, - ALC221_FIXUP_HP_FRONT_MIC, - ALC292_FIXUP_TPT460, - ALC298_FIXUP_SPK_VOLUME, - ALC298_FIXUP_LENOVO_SPK_VOLUME, - ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, - ALC269_FIXUP_ATIV_BOOK_8, - ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE, - ALC221_FIXUP_HP_MIC_NO_PRESENCE, - ALC256_FIXUP_ASUS_HEADSET_MODE, - ALC256_FIXUP_ASUS_MIC, - ALC256_FIXUP_ASUS_AIO_GPIO2, - ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, - ALC233_FIXUP_LENOVO_MULTI_CODECS, - ALC233_FIXUP_ACER_HEADSET_MIC, - ALC294_FIXUP_LENOVO_MIC_LOCATION, - ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, - ALC225_FIXUP_S3_POP_NOISE, - ALC700_FIXUP_INTEL_REFERENCE, - ALC274_FIXUP_DELL_BIND_DACS, - ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, - ALC298_FIXUP_TPT470_DOCK_FIX, - ALC298_FIXUP_TPT470_DOCK, - ALC255_FIXUP_DUMMY_LINEOUT_VERB, - ALC255_FIXUP_DELL_HEADSET_MIC, - ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, - ALC298_FIXUP_HUAWEI_MBX_STEREO, - ALC295_FIXUP_HP_X360, - ALC221_FIXUP_HP_HEADSET_MIC, - ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, - ALC295_FIXUP_HP_AUTO_MUTE, - ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, - ALC294_FIXUP_ASUS_MIC, - ALC294_FIXUP_ASUS_HEADSET_MIC, - ALC294_FIXUP_ASUS_SPK, - ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - ALC255_FIXUP_ACER_HEADSET_MIC, - ALC295_FIXUP_CHROME_BOOK, - ALC225_FIXUP_HEADSET_JACK, - ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE, - ALC225_FIXUP_WYSE_AUTO_MUTE, - ALC225_FIXUP_WYSE_DISABLE_MIC_VREF, - ALC286_FIXUP_ACER_AIO_HEADSET_MIC, - ALC256_FIXUP_ASUS_HEADSET_MIC, - ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC255_FIXUP_PREDATOR_SUBWOOFER, - ALC299_FIXUP_PREDATOR_SPK, - ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, - ALC289_FIXUP_DELL_SPK1, - ALC289_FIXUP_DELL_SPK2, - ALC289_FIXUP_DUAL_SPK, - ALC289_FIXUP_RTK_AMP_DUAL_SPK, - ALC294_FIXUP_SPK2_TO_DAC1, - ALC294_FIXUP_ASUS_DUAL_SPK, - ALC285_FIXUP_THINKPAD_X1_GEN7, - ALC285_FIXUP_THINKPAD_HEADSET_JACK, - ALC294_FIXUP_ASUS_ALLY, - ALC294_FIXUP_ASUS_ALLY_PINS, - ALC294_FIXUP_ASUS_ALLY_VERBS, - ALC294_FIXUP_ASUS_ALLY_SPEAKER, - ALC294_FIXUP_ASUS_HPE, - ALC294_FIXUP_ASUS_COEF_1B, - ALC294_FIXUP_ASUS_GX502_HP, - ALC294_FIXUP_ASUS_GX502_PINS, - ALC294_FIXUP_ASUS_GX502_VERBS, - ALC294_FIXUP_ASUS_GU502_HP, - ALC294_FIXUP_ASUS_GU502_PINS, - ALC294_FIXUP_ASUS_GU502_VERBS, - ALC294_FIXUP_ASUS_G513_PINS, - ALC285_FIXUP_ASUS_G533Z_PINS, - ALC285_FIXUP_HP_GPIO_LED, - ALC285_FIXUP_HP_MUTE_LED, - ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED, - ALC285_FIXUP_HP_BEEP_MICMUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_COEFBIT2, - ALC236_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_HP_MUTE_LED, - ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, - ALC236_FIXUP_LENOVO_INV_DMIC, - ALC298_FIXUP_SAMSUNG_AMP, - ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, - ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, - ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, - ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, - ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS, - ALC269VC_FIXUP_ACER_HEADSET_MIC, - ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, - ALC289_FIXUP_ASUS_GA401, - ALC289_FIXUP_ASUS_GA502, - ALC256_FIXUP_ACER_MIC_NO_PRESENCE, - ALC285_FIXUP_HP_GPIO_AMP_INIT, - ALC269_FIXUP_CZC_B20, - ALC269_FIXUP_CZC_TMI, - ALC269_FIXUP_CZC_L101, - ALC269_FIXUP_LEMOTE_A1802, - ALC269_FIXUP_LEMOTE_A190X, - ALC256_FIXUP_INTEL_NUC8_RUGGED, - ALC233_FIXUP_INTEL_NUC8_DMIC, - ALC233_FIXUP_INTEL_NUC8_BOOST, - ALC256_FIXUP_INTEL_NUC10, - ALC255_FIXUP_XIAOMI_HEADSET_MIC, - ALC274_FIXUP_HP_MIC, - ALC274_FIXUP_HP_HEADSET_MIC, - ALC274_FIXUP_HP_ENVY_GPIO, - ALC274_FIXUP_ASUS_ZEN_AIO_27, - ALC256_FIXUP_ASUS_HPE, - ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - ALC287_FIXUP_HP_GPIO_LED, - ALC256_FIXUP_HP_HEADSET_MIC, - ALC245_FIXUP_HP_GPIO_LED, - ALC236_FIXUP_DELL_AIO_HEADSET_MIC, - ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST, - ALC256_FIXUP_ACER_HEADSET_MIC, - ALC285_FIXUP_IDEAPAD_S740_COEF, - ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST, - ALC295_FIXUP_ASUS_DACS, - ALC295_FIXUP_HP_OMEN, - ALC285_FIXUP_HP_SPECTRE_X360, - ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, - ALC623_FIXUP_LENOVO_THINKSTATION_P340, - ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, - ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST, - ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS, - ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, - ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, - ALC298_FIXUP_LENOVO_C940_DUET7, - ALC287_FIXUP_13S_GEN2_SPEAKERS, - ALC256_FIXUP_SET_COEF_DEFAULTS, - ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - ALC233_FIXUP_NO_AUDIO_JACK, - ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, - ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, - ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, - ALC287_FIXUP_LEGION_16ACHG6, - ALC287_FIXUP_CS35L41_I2C_2, - ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, - ALC287_FIXUP_CS35L41_I2C_4, - ALC245_FIXUP_CS35L41_SPI_1, - ALC245_FIXUP_CS35L41_SPI_2, - ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED, - ALC245_FIXUP_CS35L41_SPI_4, - ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, - ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, - ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE, - ALC287_FIXUP_LEGION_16ITHG6, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, - ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, - ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN, - ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS, - ALC236_FIXUP_DELL_DUAL_CODECS, - ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, - ALC287_FIXUP_TAS2781_I2C, - ALC245_FIXUP_TAS2781_SPI_2, - ALC287_FIXUP_TXNW2781_I2C, - ALC287_FIXUP_YOGA7_14ARB7_I2C, - ALC245_FIXUP_HP_MUTE_LED_COEFBIT, - ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, - ALC245_FIXUP_HP_X360_MUTE_LEDS, - ALC287_FIXUP_THINKPAD_I2S_SPK, - ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, - ALC2XX_FIXUP_HEADSET_MIC, - ALC289_FIXUP_DELL_CS35L41_SPI_2, - ALC294_FIXUP_CS35L41_I2C_2, - ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, - ALC256_FIXUP_HEADPHONE_AMP_VOL, - ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, - ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX, - ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A, - ALC285_FIXUP_ASUS_GA403U, - ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC, - ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1, - ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, - ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1, - ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, - ALC256_FIXUP_CHROME_BOOK, - ALC245_FIXUP_CLEVO_NOISY_MIC, - ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, - ALC233_FIXUP_MEDION_MTL_SPK, - ALC294_FIXUP_BASS_SPEAKER_15, - ALC283_FIXUP_DELL_HP_RESUME, - ALC294_FIXUP_ASUS_CS35L41_SPI_2, - ALC274_FIXUP_HP_AIO_BIND_DACS, - ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, - ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, - ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, - ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, -}; - -/* A special fixup for Lenovo C940 and Yoga Duet 7; - * both have the very same PCI SSID, and we need to apply different fixups - * depending on the codec ID - */ -static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - int id; - - if (codec->core.vendor_id == 0x10ec0298) - id = ALC298_FIXUP_LENOVO_SPK_VOLUME; /* C940 */ - else - id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* Duet 7 */ - __snd_hda_apply_fixup(codec, id, action, 0); -} - -static const struct hda_fixup alc269_fixups[] = { - [ALC269_FIXUP_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_gpio2, - }, - [ALC269_FIXUP_SONY_VAIO] = { - .type = HDA_FIXUP_PINCTLS, - .v.pins = (const struct hda_pintbl[]) { - {0x19, PIN_VREFGRD}, - {} - } - }, - [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc275_fixup_gpio4_off, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC269_FIXUP_DELL_M101Z] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 13}, - {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, - {} - } - }, - [ALC269_FIXUP_SKU_IGNORE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, - }, - [ALC269_FIXUP_ASUS_G73JW] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x99130111 }, /* subwoofer */ - { } - } - }, - [ALC269_FIXUP_ASUS_N7601ZM_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03A11050 }, - { 0x1a, 0x03A11C30 }, - { 0x21, 0x03211420 }, - { } - } - }, - [ALC269_FIXUP_ASUS_N7601ZM] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x20, AC_VERB_SET_COEF_INDEX, 0x62}, - {0x20, AC_VERB_SET_PROC_COEF, 0xa007}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x10}, - {0x20, AC_VERB_SET_PROC_COEF, 0x8420}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x0f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x7774}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_N7601ZM_PINS, - }, - [ALC269_FIXUP_LENOVO_EAPD] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } - }, - [ALC275_FIXUP_SONY_HWEQ] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hweq, - .chained = true, - .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - }, - [ALC275_FIXUP_SONY_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_SONY_VAIO - }, - [ALC271_FIXUP_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc271_fixup_dmic, - }, - [ALC269_FIXUP_PCM_44K] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pcm_44k, - .chained = true, - .chain_id = ALC269_FIXUP_QUANTA_MUTE - }, - [ALC269_FIXUP_STEREO_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_stereo_dmic, - }, - [ALC269_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, - }, - [ALC269_FIXUP_QUANTA_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_quanta_mute, - }, - [ALC269_FIXUP_LIFEBOOK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x2101103f }, /* dock line-out */ - { 0x1b, 0x23a11040 }, /* dock mic-in */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_QUANTA_MUTE - }, - [ALC269_FIXUP_LIFEBOOK_EXTMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1903c }, /* headset mic, with jack detect */ - { } - }, - }, - [ALC269_FIXUP_LIFEBOOK_HP_PIN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x0221102f }, /* HP out */ - { } - }, - }, - [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_no_hp_to_lineout, - }, - [ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_U7x7_headset_mic, - }, - [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */ - { 0x1b, 0x90170152 }, /* use as internal speaker (back) */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170150 }, /* use as internal speaker */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x03a19020 }, /* headset mic */ - { 0x1b, 0x90170150 }, /* speaker */ - { } - }, - }, - [ALC269_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { } - }, - }, - [ALC269_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* mic */ - { } - }, - }, - [ALC269VB_FIXUP_AMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269VB_FIXUP_DMIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC269_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic1, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic2, - }, - [ALC269_FIXUP_HP_MUTE_LED_MIC3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_mute_led_mic3, - .chained = true, - .chain_id = ALC295_FIXUP_HP_AUTO_MUTE - }, - [ALC269_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_gpio_led, - }, - [ALC269_FIXUP_HP_GPIO_MIC1_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_gpio_mic1_led, - }, - [ALC269_FIXUP_HP_LINE1_MIC1_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_hp_line1_mic1_led, - }, - [ALC269_FIXUP_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - }, - [ALC269_FIXUP_NO_SHUTUP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_shutup, - }, - [ALC269_FIXUP_LENOVO_DOCK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x23a11040 }, /* dock mic */ - { 0x1b, 0x2121103f }, /* dock headphone */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT - }, - [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_LENOVO_DOCK, - }, - [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_pincfg_no_hp_to_lineout, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x21014020 }, /* dock line out */ - { 0x19, 0x21a19030 }, /* dock mic */ - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_no_hp_mic, - }, - [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x12, 0x90a60130}, - {0x13, 0x40000000}, - {0x14, 0x90170110}, - {0x18, 0x411111f0}, - {0x19, 0x04a11040}, - {0x1a, 0x411111f0}, - {0x1b, 0x90170112}, - {0x1d, 0x40759a05}, - {0x1e, 0x411111f0}, - {0x21, 0x04211020}, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC298_FIXUP_HUAWEI_MBX_STEREO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_huawei_mbx_stereo, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC269_FIXUP_ASUS_X101_FUNC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_x101_headset_mic, - }, - [ALC269_FIXUP_ASUS_X101_VERB] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0310}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_X101_FUNC - }, - [ALC269_FIXUP_ASUS_X101] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x04a1182c }, /* Headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_ASUS_X101_VERB - }, - [ALC271_FIXUP_AMIC_MIC2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x19, 0x01a19c20 }, /* mic */ - { 0x1b, 0x99a7012f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } - }, - }, - [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc271_hp_gate_mic_jack, - .chained = true, - .chain_id = ALC271_FIXUP_AMIC_MIC2, - }, - [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK, - }, - [ALC269_FIXUP_ACER_AC700] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x03a11c20 }, /* mic */ - { 0x1e, 0x0346101e }, /* SPDIF1 */ - { 0x21, 0x0321101f }, /* HP out */ - { } - }, - .chained = true, - .chain_id = ALC271_FIXUP_DMIC, - }, - [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269VB_FIXUP_ASUS_ZENBOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269VB_FIXUP_DMIC, - }, - [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* class-D output amp +5dB */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 }, - {} - }, - .chained = true, - .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK, - }, - [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a110f0 }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1, - }, - [ALC269VB_FIXUP_ORDISSIMO_EVE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x99a3092f }, /* int-mic */ - { 0x18, 0x03a11d20 }, /* mic */ - { 0x19, 0x411111f0 }, /* Unused bogus pin */ - { } - }, - }, - [ALC283_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_chromebook, - }, - [ALC283_FIXUP_SENSE_COMBO_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_sense_combo_jack, - .chained = true, - .chain_id = ALC283_FIXUP_CHROME_BOOK, - }, - [ALC282_FIXUP_ASUS_TX300] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc282_fixup_asus_tx300, - }, - [ALC283_FIXUP_INT_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x20, AC_VERB_SET_COEF_INDEX, 0x1a}, - {0x20, AC_VERB_SET_PROC_COEF, 0x0011}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC290_FIXUP_SUBWOOFER_HSJACK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, - }, - [ALC290_FIXUP_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170112 }, /* subwoofer */ - { } - }, - .chained = true, - .chain_id = ALC290_FIXUP_MONO_SPEAKERS, - }, - [ALC290_FIXUP_MONO_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc290_fixup_mono_speakers, - }, - [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc290_fixup_mono_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - }, - [ALC269_FIXUP_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_thinkpad_acpi, - .chained = true, - .chain_id = ALC269_FIXUP_SKU_IGNORE, - }, - [ALC269_FIXUP_LENOVO_XPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_ideapad_acpi, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC255_FIXUP_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc255, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc255_no_hp_mic, - }, - [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC292_FIXUP_TPT440_DOCK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt440_dock, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC292_FIXUP_TPT440] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC292_FIXUP_TPT440_DOCK, - }, - [ALC283_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a110f0 }, - { }, - }, - }, - [ALC255_FIXUP_MIC_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_micmute_led, - }, - [ALC282_FIXUP_ASPIRE_V5_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x90a60130 }, - { 0x14, 0x90170110 }, - { 0x17, 0x40000008 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x01a1913c }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40f89b2d }, - { 0x1e, 0x411111f0 }, - { 0x21, 0x0321101f }, - { }, - }, - }, - [ALC269VB_FIXUP_ASPIRE_E1_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269vb_fixup_aspire_e1_coef, - }, - [ALC280_FIXUP_HP_GPIO4] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_gpio4, - }, - [ALC286_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc286_fixup_hp_gpio_led, - }, - [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_gpio2_mic_hotkey, - }, - [ALC280_FIXUP_HP_DOCK_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x21011020 }, /* line-out */ - { 0x1a, 0x01a1903c }, /* headset mic */ - { 0x18, 0x2181103f }, /* line-in */ - { }, - }, - .chained = true, - .chain_id = ALC280_FIXUP_HP_GPIO4 - }, - [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x21011020 }, /* line-out */ - { 0x18, 0x2181103f }, /* line-in */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED - }, - [ALC280_FIXUP_HP_9480M] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc280_fixup_hp_9480m, - }, - [ALC245_FIXUP_HP_X360_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_x360_amp, - .chained = true, - .chain_id = ALC245_FIXUP_HP_GPIO_LED - }, - [ALC288_FIXUP_DELL_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_dell_alc288, - .chained = true, - .chain_id = ALC255_FIXUP_MIC_MUTE_LED - }, - [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE - }, - [ALC288_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC288_FIXUP_DELL_XPS_13] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC288_FIXUP_DISABLE_AAMIX - }, - [ALC292_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE - }, - [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC292_FIXUP_DELL_E7X_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, - .chained = true, - .chain_id = ALC292_FIXUP_DISABLE_AAMIX - }, - [ALC292_FIXUP_DELL_E7X] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_micmute_led, - /* micmute fixup must be applied at last */ - .chained_before = true, - .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX, - }, - [ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* headset mic w/o jack detect */ - { } - }, - .chained_before = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC275_FIXUP_DELL_XPS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x1f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x00c0}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x30}, - {0x20, AC_VERB_SET_PROC_COEF, 0x00b1}, - {} - } - }, - [ALC293_FIXUP_LENOVO_SPK_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_lenovo_line2_mic_hotkey, - }, - [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_lenovo_low_en_micmute_led, - }, - [ALC233_FIXUP_INTEL_NUC8_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST, - }, - [ALC233_FIXUP_INTEL_NUC8_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost - }, - [ALC255_FIXUP_DELL_SPK_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC225_FIXUP_DISABLE_MIC_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_mic_vref, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Disable pass-through path for FRONT 14h */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, - {} - }, - .chained = true, - .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF - }, - [ALC280_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC, - }, - [ALC221_FIXUP_HP_FRONT_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a19020 }, /* Front Mic */ - { } - }, - }, - [ALC292_FIXUP_TPT460] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt440_dock, - .chained = true, - .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE, - }, - [ALC298_FIXUP_SPK_VOLUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_speaker_volume, - .chained = true, - .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, - }, - [ALC298_FIXUP_LENOVO_SPK_VOLUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_speaker_volume, - }, - [ALC295_FIXUP_DISABLE_DAC3] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_disable_dac3, - }, - [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC245_FIXUP_CS35L41_SPI_2 - }, - [ALC285_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170120 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_HEADSET_MIC - }, - [ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2 - }, - [ALC285_FIXUP_ASUS_I2C_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1 - }, - [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170151 }, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC269_FIXUP_ATIV_BOOK_8] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC269_FIXUP_NO_SHUTUP - }, - [ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01813030 }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_HEADSET_MODE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, - }, - [ALC256_FIXUP_ASUS_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x13, 0x90a60160 }, /* use as internal mic */ - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_AIO_GPIO2] = { - .type = HDA_FIXUP_FUNC, - /* Set up GPIO2 for the speaker amp */ - .v.func = alc_fixup_gpio4, - }, - [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x40}, - {0x20, AC_VERB_SET_PROC_COEF, 0x8800}, - {} - }, - .chained = true, - .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE - }, - [ALC233_FIXUP_LENOVO_MULTI_CODECS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_alc662_fixup_lenovo_dual_codecs, - .chained = true, - .chain_id = ALC269_FIXUP_GPIO2 - }, - [ALC233_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - .chained = true, - .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE - }, - [ALC294_FIXUP_LENOVO_MIC_LOCATION] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* Change the mic location from front to right, otherwise there are - two front mics with the same name, pulseaudio can't handle them. - This is just a temporary workaround, after applying this fixup, - there will be one "Front Mic" and one "Mic" in this machine. - */ - { 0x1a, 0x04a19040 }, - { } - }, - }, - [ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x0101102f }, /* Rear Headset HP */ - { 0x19, 0x02a1913c }, /* use as Front headset mic, without its own jack detect */ - { 0x1a, 0x01a19030 }, /* Rear Headset MIC */ - { 0x1b, 0x02011020 }, - { } - }, - .chained = true, - .chain_id = ALC225_FIXUP_S3_POP_NOISE - }, - [ALC225_FIXUP_S3_POP_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc225_fixup_s3_pop_noise, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC700_FIXUP_INTEL_REFERENCE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Enables internal speaker */ - {0x20, AC_VERB_SET_COEF_INDEX, 0x45}, - {0x20, AC_VERB_SET_PROC_COEF, 0x5289}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x4A}, - {0x20, AC_VERB_SET_PROC_COEF, 0x001b}, - {0x58, AC_VERB_SET_COEF_INDEX, 0x00}, - {0x58, AC_VERB_SET_PROC_COEF, 0x3888}, - {0x20, AC_VERB_SET_COEF_INDEX, 0x6f}, - {0x20, AC_VERB_SET_PROC_COEF, 0x2c0b}, - {} - } - }, - [ALC274_FIXUP_DELL_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_bind_dacs, - .chained = true, - .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x0401102f }, - { } - }, - .chained = true, - .chain_id = ALC274_FIXUP_DELL_BIND_DACS - }, - [ALC298_FIXUP_TPT470_DOCK_FIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt470_dock, - .chained = true, - .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE - }, - [ALC298_FIXUP_TPT470_DOCK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_tpt470_dacs, - .chained = true, - .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX - }, - [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0201101f }, - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC255_FIXUP_DELL_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC295_FIXUP_HP_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_hp_top_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 - }, - [ALC221_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x0181313f}, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_invalidate_dacs, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC295_FIXUP_HP_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - }, - [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x13, 0x90a60160 }, /* use as internal mic */ - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1103c }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_SPK] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC295_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_chromebook, - .chained = true, - .chain_id = ALC225_FIXUP_HEADSET_JACK - }, - [ALC225_FIXUP_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - }, - [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Disable PCBEEP-IN passthrough */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE - }, - [ALC255_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11130 }, - { 0x1a, 0x90a60140 }, /* use as internal mic */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x01011020 }, /* Rear Line out */ - { 0x19, 0x01a1913c }, /* use as Front headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC225_FIXUP_WYSE_AUTO_MUTE - }, - [ALC225_FIXUP_WYSE_AUTO_MUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, - .chained = true, - .chain_id = ALC225_FIXUP_WYSE_DISABLE_MIC_VREF - }, - [ALC225_FIXUP_WYSE_DISABLE_MIC_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_mic_vref, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5029 }, - { } - }, - .chained = true, - .chain_id = ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE - }, - [ALC256_FIXUP_ASUS_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11020 }, /* headset mic with jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC256_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC255_FIXUP_PREDATOR_SUBWOOFER] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */ - { 0x1b, 0x90170152 } /* use as internal speaker (back) */ - } - }, - [ALC299_FIXUP_PREDATOR_SPK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x90170150 }, /* use as headset mic, without its own jack detect */ - { } - } - }, - [ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC255_FIXUP_PREDATOR_SUBWOOFER - }, - [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x04a11040 }, - { 0x21, 0x04211020 }, - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC289_FIXUP_DELL_SPK1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170140 }, - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE - }, - [ALC289_FIXUP_DELL_SPK2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x90170130 }, /* bass spk */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE - }, - [ALC289_FIXUP_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC289_FIXUP_DELL_SPK2 - }, - [ALC289_FIXUP_RTK_AMP_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC289_FIXUP_DELL_SPK1 - }, - [ALC294_FIXUP_SPK2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_DUAL_SPK] = { - .type = HDA_FIXUP_FUNC, - /* The GPIO must be pulled to initialize the AMP */ - .v.func = alc_fixup_gpio4, - .chained = true, - .chain_id = ALC294_FIXUP_SPK2_TO_DAC1 - }, - [ALC294_FIXUP_ASUS_ALLY] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS - }, - [ALC294_FIXUP_ASUS_ALLY_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1a, 0x03a11c30 }, - { 0x21, 0x03211420 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_VERBS - }, - [ALC294_FIXUP_ASUS_ALLY_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x46 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0004 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x47 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xa47a }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0049}, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x201b }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4278}, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER - }, - [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - }, - [ALC285_FIXUP_THINKPAD_X1_GEN7] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_thinkpad_x1_gen7, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7 - }, - [ALC294_FIXUP_ASUS_HPE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC294_FIXUP_ASUS_GX502_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* front HP mic */ - { 0x1a, 0x01a11830 }, /* rear external mic */ - { 0x21, 0x03211020 }, /* front HP out */ - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GX502_VERBS - }, - [ALC294_FIXUP_ASUS_GX502_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* set 0x15 to HP-OUT ctrl */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* unmute the 0x15 amp */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GX502_HP - }, - [ALC294_FIXUP_ASUS_GX502_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_gx502_hp, - }, - [ALC294_FIXUP_ASUS_GU502_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a11050 }, /* rear HP mic */ - { 0x1a, 0x01a11830 }, /* rear external mic */ - { 0x21, 0x012110f0 }, /* rear HP out */ - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GU502_VERBS - }, - [ALC294_FIXUP_ASUS_GU502_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* set 0x15 to HP-OUT ctrl */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* unmute the 0x15 amp */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, - /* set 0x1b to HP-OUT */ - { 0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_GU502_HP - }, - [ALC294_FIXUP_ASUS_GU502_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_gu502_hp, - }, - [ALC294_FIXUP_ASUS_G513_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* front HP mic */ - { 0x1a, 0x03a11c30 }, /* rear external mic */ - { 0x21, 0x03211420 }, /* front HP out */ - { } - }, - }, - [ALC285_FIXUP_ASUS_G533Z_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90170152 }, /* Speaker Surround Playback Switch */ - { 0x19, 0x03a19020 }, /* Mic Boost Volume */ - { 0x1a, 0x03a11c30 }, /* Mic Boost Volume */ - { 0x1e, 0x90170151 }, /* Rear jack, IN OUT EAPD Detect */ - { 0x21, 0x03211420 }, - { } - }, - }, - [ALC294_FIXUP_ASUS_COEF_1B] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set bit 10 to correct noisy output after reboot from - * Windows 10 (due to pop noise reduction?) - */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b }, - { } - }, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA401, - }, - [ALC285_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_gpio_led, - }, - [ALC285_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_mute_led, - }, - [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_mute_led, - }, - [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_beep, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_coefbit2, - }, - [ALC236_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_gpio_led, - }, - [ALC236_FIXUP_HP_MUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led, - }, - [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc236_fixup_hp_mute_led_micmute_vref, - }, - [ALC236_FIXUP_LENOVO_INV_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, - .chained = true, - .chain_id = ALC283_FIXUP_INT_MIC, - }, - [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_hp_mute_led_coefbit11, - }, - [ALC298_FIXUP_SAMSUNG_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp, - .chained = true, - .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET - }, - [ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp_v2_2_amps - }, - [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_samsung_amp_v2_4_amps - }, - [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 }, - { } - }, - }, - [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x08}, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf}, - { } - }, - }, - [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x90100120 }, /* use as internal speaker */ - { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x01011020 }, /* use as line out */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269VC_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x02a11030 }, /* use as headset mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MIC - }, - [ALC289_FIXUP_ASUS_GA401] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc289_fixup_asus_ga401, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA502, - }, - [ALC289_FIXUP_ASUS_GA502] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11020 }, /* headset mic with jack detect */ - { } - }, - }, - [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE - }, - [ALC285_FIXUP_HP_GPIO_AMP_INIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_gpio_amp_init, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED - }, - [ALC269_FIXUP_CZC_B20] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x411111f0 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x15, 0x032f1020 }, /* HP out */ - { 0x17, 0x411111f0 }, - { 0x18, 0x03ab1040 }, /* mic */ - { 0x19, 0xb7a7013f }, - { 0x1a, 0x0181305f }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x411111f0 }, - { 0x1e, 0x411111f0 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_CZC_TMI] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x4000c000 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x15, 0x0421401f }, /* HP out */ - { 0x17, 0x411111f0 }, - { 0x18, 0x04a19020 }, /* mic */ - { 0x19, 0x411111f0 }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40448505 }, - { 0x1e, 0x411111f0 }, - { 0x20, 0x8000ffff }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_CZC_L101] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x40000000 }, - { 0x14, 0x01014010 }, /* speaker */ - { 0x15, 0x411111f0 }, /* HP out */ - { 0x16, 0x411111f0 }, - { 0x18, 0x01a19020 }, /* mic */ - { 0x19, 0x02a19021 }, - { 0x1a, 0x0181302f }, - { 0x1b, 0x0221401f }, - { 0x1c, 0x411111f0 }, - { 0x1d, 0x4044c601 }, - { 0x1e, 0x411111f0 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_LEMOTE_A1802] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0x40000000 }, - { 0x14, 0x90170110 }, /* speaker */ - { 0x17, 0x411111f0 }, - { 0x18, 0x03a19040 }, /* mic1 */ - { 0x19, 0x90a70130 }, /* mic2 */ - { 0x1a, 0x411111f0 }, - { 0x1b, 0x411111f0 }, - { 0x1d, 0x40489d2d }, - { 0x1e, 0x411111f0 }, - { 0x20, 0x0003ffff }, - { 0x21, 0x03214020 }, - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC269_FIXUP_LEMOTE_A190X] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121401f }, /* HP out */ - { 0x18, 0x01a19c20 }, /* rear mic */ - { 0x19, 0x99a3092f }, /* front mic */ - { 0x1b, 0x0201401f }, /* front lineout */ - { } - }, - .chain_id = ALC269_FIXUP_DMIC, - }, - [ALC256_FIXUP_INTEL_NUC8_RUGGED] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC256_FIXUP_INTEL_NUC10] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_XIAOMI_HEADSET_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA502 - }, - [ALC274_FIXUP_HP_MIC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, - { } - }, - }, - [ALC274_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_headset_mic, - .chained = true, - .chain_id = ALC274_FIXUP_HP_MIC - }, - [ALC274_FIXUP_HP_ENVY_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_envy_gpio, - }, - [ALC274_FIXUP_ASUS_ZEN_AIO_27] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x202b }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 }, - {} - }, - .chained = true, - .chain_id = ALC2XX_FIXUP_HEADSET_MIC, - }, - [ALC256_FIXUP_ASUS_HPE] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set EAPD high */ - { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 }, - { } - }, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC - }, - [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_jack, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC287_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_hp_gpio_led, - }, - [ALC256_FIXUP_HP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_headset_mic, - }, - [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_int_mic, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE - }, - [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x411111f0 }, - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { }, - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE - }, - [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - }, - [ALC256_FIXUP_ACER_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1113c }, /* use as headset mic, without its own jack detect */ - { 0x1a, 0x90a1092f }, /* use as internal mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC285_FIXUP_IDEAPAD_S740_COEF] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC295_FIXUP_ASUS_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_asus_dacs, - }, - [ALC295_FIXUP_HP_OMEN] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x12, 0xb7a60130 }, - { 0x13, 0x40000000 }, - { 0x14, 0x411111f0 }, - { 0x16, 0x411111f0 }, - { 0x17, 0x90170110 }, - { 0x18, 0x411111f0 }, - { 0x19, 0x02a11030 }, - { 0x1a, 0x411111f0 }, - { 0x1b, 0x04a19030 }, - { 0x1d, 0x40600001 }, - { 0x1e, 0x411111f0 }, - { 0x21, 0x03211020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HP_LINE1_MIC1_LED, - }, - [ALC285_FIXUP_HP_SPECTRE_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360, - }, - [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_eb1 - }, - [ALC285_FIXUP_HP_SPECTRE_X360_DF1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_spectre_x360_df1 - }, - [ALC285_FIXUP_HP_ENVY_X360] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_hp_envy_x360, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, - }, - [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_shutup, - .chained = true, - .chain_id = ALC283_FIXUP_HEADSET_MIC, - }, - [ALC255_FIXUP_ACER_HEADPHONE_AND_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x21, 0x03211030 }, /* Change the Headphone location to Left */ - { } - }, - .chained = true, - .chain_id = ALC255_FIXUP_XIAOMI_HEADSET_MIC - }, - [ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, - }, - [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_ideapad_s740_coef, - .chained = true, - .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, - }, - [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_15imhg05_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI, - }, - [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - //.v.verbs = legion_15imhg05_coefs, - .v.verbs = (const struct hda_verb[]) { - // set left speaker Legion 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // set right speaker Legion 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, - }, - [ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_15imhg05_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC287_FIXUP_YOGA7_14ITL_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - // set left speaker Yoga 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // set right speaker Yoga 7i. - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC298_FIXUP_LENOVO_C940_DUET7] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc298_fixup_lenovo_c940_duet7, - }, - [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - {} - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE, - }, - [ALC256_FIXUP_SET_COEF_DEFAULTS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_set_coef_defaults, - }, - [ALC245_FIXUP_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_gpio_led, - }, - [ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11120 }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, - }, - [ALC233_FIXUP_NO_AUDIO_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc233_fixup_no_audio_jack, - }, - [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_mic_no_presence_and_resume, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC287_FIXUP_LEGION_16ACHG6] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_16achg6_speakers, - }, - [ALC287_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, - [ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC287_FIXUP_CS35L41_I2C_4] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_four, - }, - [ALC245_FIXUP_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - }, - [ALC245_FIXUP_CS35L41_SPI_1] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_one, - }, - [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC245_FIXUP_CS35L41_SPI_4] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_four, - }, - [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_four, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_HP_MUTE_LED, - }, - [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell4_mic_no_presence_quiet, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - }, - [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, - [ALC287_FIXUP_LEGION_16ITHG6] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_legion_16ithg6_speakers, - }, - [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - // enable left speaker - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x40 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - // enable right speaker - { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x44 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, - - { }, - }, - }, - [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, - .chained = true, - .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, - }, - [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2, - }, - [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc295_fixup_dell_inspiron_top_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - }, - [ALC236_FIXUP_DELL_DUAL_CODECS] = { - .type = HDA_FIXUP_PINS, - .v.func = alc1220_fixup_gb_dual_codecs, - .chained = true, - .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - }, - [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - }, - [ALC287_FIXUP_TAS2781_I2C] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_tias_i2c, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC245_FIXUP_TAS2781_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_spi, - .chained = true, - .chain_id = ALC285_FIXUP_HP_GPIO_LED, - }, - [ALC287_FIXUP_TXNW2781_I2C] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_txnw_i2c, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { - .type = HDA_FIXUP_FUNC, - .v.func = yoga7_14arb7_fixup_i2c, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, - }, - [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_coefbit, - }, - [ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_v1_coefbit, - }, - [ALC245_FIXUP_HP_X360_MUTE_LEDS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_mute_led_coefbit, - .chained = true, - .chain_id = ALC245_FIXUP_HP_GPIO_LED - }, - [ALC287_FIXUP_THINKPAD_I2S_SPK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_bind_dacs, - .chained = true, - .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - }, - [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_bind_dacs, - .chained = true, - .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, - }, - [ALC2XX_FIXUP_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mic, - }, - [ALC289_FIXUP_DELL_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC289_FIXUP_DUAL_SPK - }, - [ALC294_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, - [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_acer_sfg16_micmute_led, - }, - [ALC256_FIXUP_HEADPHONE_AMP_VOL] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_decrease_headphone_amp_val, - }, - [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_spectre_x360_eu0xxx, - }, - [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx, - }, - [ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc245_fixup_hp_zbook_firefly_g12a, - }, - [ALC285_FIXUP_ASUS_GA403U] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_asus_ga403u, - }, - [ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, - }, - [ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - }, - [ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA403U, - }, - [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318, - .chained = true, - .chain_id = ALC269_FIXUP_THINKPAD_ACPI - }, - [ALC256_FIXUP_CHROME_BOOK] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc256_fixup_chromebook, - .chained = true, - .chain_id = ALC225_FIXUP_HEADSET_JACK - }, - [ALC245_FIXUP_CLEVO_NOISY_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, - }, - [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x20a11040 }, /* dock mic */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST - }, - [ALC233_FIXUP_MEDION_MTL_SPK] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x90170110 }, - { } - }, - }, - [ALC294_FIXUP_BASS_SPEAKER_15] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc294_fixup_bass_speaker_15, - }, - [ALC283_FIXUP_DELL_HP_RESUME] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc283_fixup_dell_hp_resume, - }, - [ALC294_FIXUP_ASUS_CS35L41_SPI_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_spi_two, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC, - }, - [ALC274_FIXUP_HP_AIO_BIND_DACS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc274_fixup_hp_aio_bind_dacs, - }, - [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, - { 0x1b, 0x03a11c30 }, - { } - }, - .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, - }, - [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost, - .chained = true, - .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, - }, -}; - -static const struct hda_quirk alc269_fixup_tbl[] = { - SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), - SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), - SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), - SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), - SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF), - SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x1025, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), - SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), - SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), - SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), - SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x129c, "Acer SWIFT SF314-55", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x129d, "Acer SWIFT SF313-51", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1300, "Acer SWIFT SF314-56", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), - SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), - SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), - SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME), - SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X), - SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), - SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13), - SND_PCI_QUIRK(0x1028, 0x0669, "Dell Optiplex 9020m", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x069a, "Dell Vostro 5480", ALC290_FIXUP_SUBWOOFER_HSJACK), - SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), - SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), - SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3), - SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), - SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET), - SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC), - SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0a9d, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), - SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), - SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc0, "Dell Oasis 13", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK), - SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), - SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY), - SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS), - SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS), - SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M), - SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), - SND_PCI_QUIRK(0x103c, 0x2b5e, "HP 288 Pro G2 MT", ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x802e, "HP Z240 SFF", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), - SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), - SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), - SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), - SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), - SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN), - SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), - SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), - SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - 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, 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), - SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), - SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1), - SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8760, "HP EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED), - SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation", - ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation", - ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f2, "HP ProBook 640 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), - SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), - SND_PCI_QUIRK(0x103c, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), - SND_PCI_QUIRK(0x103c, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), - SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c0, "HP ZBook Power 15.6 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - 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, 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), - SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), - SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8a, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8b, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - 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, 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), - SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - 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, 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), - SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), - SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), - SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d07, "HP Victus 15-fb2xxx (MB 8D07)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), - SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), - SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), - SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), - SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8ded, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dfb, "HP EliteBook 6 G1a 14", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), - SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), - SND_PCI_QUIRK(0x103c, 0x8e1d, "HP ZBook X Gli 16 G12", ALC236_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), - SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), - 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(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), - SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), - SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), - SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), - SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), - SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM), - SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - 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, 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), - SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1493, "ASUS GV601VV/VU/VJ/VQ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G614JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS G513PI/PU/PV", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x14f2, "ASUS VivoBook X515JA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1503, "ASUS G733PY/PZ/PZV/PYV", ALC287_FIXUP_CS35L41_I2C_2), - 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, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), - SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS UX7602VI/BZ", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS), - SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), - SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY), - SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), - SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), - SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1c80, "ASUS VivoBook TP401", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS), - SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1ccf, "ASUS G814JU/JV/JI", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1cdf, "ASUS G814JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1cef, "ASUS G834JY/JZ/JI/JG", ALC285_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS G713PI/PU/PV/PVN", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), - SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), - SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), - SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), - SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS), - SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), - SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), - SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), - SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), - SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), - SND_PCI_QUIRK(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1), - SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), - SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), - SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), - SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT), - SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN), - SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC), - SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), - SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), - SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), - SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), - SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - 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(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), - SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), - SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), - SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), - SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP), - SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), - SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), - SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK), - SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x35a1, "Clevo V3[56]0EN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x35b1, "Clevo V3[57]0WN[MNP]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x4041, "Clevo NV4[15]PZ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5015, "Clevo NH5[58]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5017, "Clevo NH7[79]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50e1, "Clevo NH5[58]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50e2, "Clevo NH7[79]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f2, "Clevo NH50E[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f5, "Clevo NH55EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x50f6, "Clevo NH55DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f3, "Clevo NH77DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f4, "Clevo NH77EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x70f6, "Clevo NH77DPQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x7724, "Clevo L140AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8550, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8551, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8560, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x867c, "Clevo NP7[01]PNP", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), - SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x9600, "Clevo N960K[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - 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, 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), - SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC), - SND_PCI_QUIRK(0x1558, 0xa743, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), - SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), - SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), - SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST), - SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440), - SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x222d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), - SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), - SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), - SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), - SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), - SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), - SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), - SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), - HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC), - SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), - SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), - SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), - SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), - SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), - HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ - SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), - SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), - 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), - 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), - SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), - SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), - SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), - SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), - SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), - SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), - SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), - SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460), - SND_PCI_QUIRK(0x17aa, 0x505d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), - SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK), - SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), - SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), - SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL), - SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), - SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), - SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), - SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), - SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), - SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), - SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ - SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802), - SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X), - SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS), - SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), - SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), - SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), - SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), - SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), - SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), - SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), - SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), - SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), - SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), - SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), - SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), - SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), - SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), - SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), - SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), - -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC), - SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC), - SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC), -#endif - {} -}; - -static const struct hda_quirk alc269_fixup_vendor_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), - SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), - SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), - SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), - {} -}; - -static const struct hda_model_fixup alc269_fixup_models[] = { - {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, - {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, - {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, - {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"}, - {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, - {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, - {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, - {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, - {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"}, - {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, - {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, - {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, - {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, - {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, - {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, - {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"}, - {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, - {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, - {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, - {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, - {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, - {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"}, - {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, - {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, - {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, - {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"}, - {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"}, - {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"}, - {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"}, - {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"}, - {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"}, - {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"}, - {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"}, - {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"}, - {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"}, - {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"}, - {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"}, - {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"}, - {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"}, - {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"}, - {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"}, - {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"}, - {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"}, - {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"}, - {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"}, - {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"}, - {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"}, - {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"}, - {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"}, - {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"}, - {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"}, - {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"}, - {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"}, - {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, - {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, - {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, - {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"}, - {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, - {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, - {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, - {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, - {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, - {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, - {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, - {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"}, - {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, - {.id = ALC269VB_FIXUP_ASPIRE_E1_COEF, .name = "aspire-e1-coef"}, - {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, - {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, - {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"}, - {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"}, - {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"}, - {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"}, - {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"}, - {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"}, - {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"}, - {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"}, - {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"}, - {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"}, - {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"}, - {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"}, - {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, - {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, - {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, - {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"}, - {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"}, - {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"}, - {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"}, - {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"}, - {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"}, - {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"}, - {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"}, - {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"}, - {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"}, - {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"}, - {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"}, - {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"}, - {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"}, - {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"}, - {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"}, - {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"}, - {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, - {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, - {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, - {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"}, - {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"}, - {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"}, - {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"}, - {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"}, - {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"}, - {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"}, - {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, - {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, - {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, - {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"}, - {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"}, - {.id = ALC285_FIXUP_HP_SPECTRE_X360_DF1, .name = "alc285-hp-spectre-x360-df1"}, - {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"}, - {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"}, - {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"}, - {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"}, - {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"}, - {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"}, - {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, - {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, - {} -}; -#define ALC225_STANDARD_PINS \ - {0x21, 0x04211020} - -#define ALC256_STANDARD_PINS \ - {0x12, 0x90a60140}, \ - {0x14, 0x90170110}, \ - {0x21, 0x02211020} - -#define ALC282_STANDARD_PINS \ - {0x14, 0x90170110} - -#define ALC290_STANDARD_PINS \ - {0x12, 0x99a30130} - -#define ALC292_STANDARD_PINS \ - {0x14, 0x90170110}, \ - {0x15, 0x0221401f} - -#define ALC295_STANDARD_PINS \ - {0x12, 0xb7a60130}, \ - {0x14, 0x90170110}, \ - {0x21, 0x04211020} - -#define ALC298_STANDARD_PINS \ - {0x12, 0x90a60130}, \ - {0x21, 0x03211020} - -static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0221, 0x103c, "HP Workstation", ALC221_FIXUP_HP_HEADSET_MIC, - {0x14, 0x01014020}, - {0x17, 0x90170110}, - {0x18, 0x02a11030}, - {0x19, 0x0181303F}, - {0x21, 0x0221102f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE, - {0x12, 0x90a601c0}, - {0x14, 0x90171120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x1a, 0x90a70130}, - {0x1b, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x14, 0x901701a0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x14, 0x901701b0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60150}, - {0x14, 0x901701a0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60150}, - {0x14, 0x901701b0}), - SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x1b, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0233, 0x8086, "Intel NUC Skull Canyon", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x1b, 0x01111010}, - {0x1e, 0x01451130}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x1a, 0x02a11040}, - {0x1b, 0x01014020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11030}, - {0x1a, 0x02a11040}, - {0x1b, 0x01011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x1a, 0x02a11030}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC, - {0x21, 0x02211010}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x02011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x01011020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x01014020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x01011020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170130}, - {0x1b, 0x02011020}, - {0x21, 0x0221103f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170150}, - {0x1b, 0x02011020}, - {0x21, 0x0221105f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x1b, 0x01014020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x17, 0x90170140}, - {0x21, 0x0321102f}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170140}, - {0x21, 0x02211050}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90171130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60170}, - {0x14, 0x90170140}, - {0x21, 0x02211050}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60180}, - {0x14, 0x90170130}, - {0x21, 0x02211040}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60180}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x1b, 0x01011020}, - {0x21, 0x02211010}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x1a, 0x90a70130}, - {0x1b, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, - {0x14, 0x90170110}, - {0x19, 0x02a11020}, - {0x21, 0x0221101f}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC, - {0x17, 0x90170110}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED, - {0x12, 0x90a60140}, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x18, 0x02811030}, - {0x1a, 0x04a1103f}, - {0x1b, 0x02011020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11020}, - {0x21, 0x03211040}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC282_STANDARD_PINS, - {0x12, 0x99a30130}, - {0x19, 0x04a11020}, - {0x21, 0x0421101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED, - ALC282_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x19, 0x04a11030}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC282_STANDARD_PINS, - {0x12, 0x90a609c0}, - {0x18, 0x03a11830}, - {0x19, 0x04a19831}, - {0x1a, 0x0481303f}, - {0x1b, 0x04211020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, - ALC282_STANDARD_PINS, - {0x12, 0x90a60940}, - {0x18, 0x03a11830}, - {0x19, 0x04a19831}, - {0x1a, 0x0481303f}, - {0x1b, 0x04211020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC282_STANDARD_PINS, - {0x12, 0x90a60130}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60160}, - {0x14, 0x90170120}, - {0x21, 0x02211030}), - SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC282_STANDARD_PINS, - {0x12, 0x90a60130}, - {0x19, 0x03a11020}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x1d, 0x40600001}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, - {0x14, 0x90170110}, - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK, - {0x14, 0x90170110}, - {0x17, 0x90170111}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, - {0x17, 0x90170110}, - {0x19, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, - {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */ - {0x19, 0x04a11040}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x12, 0x90a60120}, - {0x14, 0x90170110}, - {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211040}, - {0x18, 0x90170112}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211040}, - {0x18, 0x90170110}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x15, 0x04211020}, - {0x1a, 0x04a11040}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x04211020}, - {0x1a, 0x04a11040}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x04211020}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, - ALC290_STANDARD_PINS, - {0x14, 0x90170110}, - {0x15, 0x0421101f}, - {0x1a, 0x04a11020}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x16, 0x01014020}, - {0x19, 0x01a19030}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}, - {0x16, 0x01014020}, - {0x18, 0x02a19031}, - {0x19, 0x01a1903e}), - SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x12, 0x90a60140}), - SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x13, 0x90a60140}, - {0x16, 0x21014020}, - {0x19, 0x21a19030}), - SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC292_STANDARD_PINS, - {0x13, 0x90a60140}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE, - {0x17, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC, - {0x14, 0x90170110}, - {0x1b, 0x90a70130}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60120}, - {0x17, 0x90170110}, - {0x21, 0x04211030}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, - {0x12, 0x90a60130}, - {0x17, 0x90170110}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170140}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC298_STANDARD_PINS, - {0x17, 0x90170150}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, - {0x12, 0xb7a60140}, - {0x13, 0xb7a60150}, - {0x17, 0x90170110}, - {0x1a, 0x03011020}, - {0x21, 0x03211030}), - SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, - {0x12, 0xb7a60140}, - {0x17, 0x90170110}, - {0x1a, 0x03a11030}, - {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - ALC225_STANDARD_PINS, - {0x12, 0xb7a60130}, - {0x17, 0x90170110}), - SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC, - {0x14, 0x01014010}, - {0x17, 0x90170120}, - {0x18, 0x02a11030}, - {0x19, 0x02a1103f}, - {0x21, 0x0221101f}), - {} -}; - -/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match - * more machines, don't need to match all valid pins, just need to match - * all the pins defined in the tbl. Just because of this reason, it is possible - * that a single machine matches multiple tbls, so there is one limitation: - * at most one tbl is allowed to define for the same vendor and same codec - */ -static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - {0x19, 0x40000000}, - {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, - {0x19, 0x40000000}, - {0x1b, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, - {0x19, 0x40000000}, - {0x1a, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC, - {0x19, 0x40000000}), - {} -}; - -static void alc269_fill_coef(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int val; - - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - - if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8817); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x016) { - alc_write_coef_idx(codec, 0xf, 0x960b); - alc_write_coef_idx(codec, 0xe, 0x8814); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { - /* Power up output pin */ - alc_update_coef_idx(codec, 0x04, 0, 1<<11); - } - - if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { - val = alc_read_coef_idx(codec, 0xd); - if (val != -1 && (val & 0x0c00) >> 10 != 0x1) { - /* Capless ramp up clock control */ - alc_write_coef_idx(codec, 0xd, val | (1<<10)); - } - val = alc_read_coef_idx(codec, 0x17); - if (val != -1 && (val & 0x01c0) >> 6 != 0x4) { - /* Class D power on reset */ - alc_write_coef_idx(codec, 0x17, val | (1<<7)); - } - } - - /* HP */ - alc_update_coef_idx(codec, 0x4, 0, 1<<11); -} - -/* - */ -static int patch_alc269(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - spec->gen.shared_mic_vref_pin = 0x18; - codec->power_save_node = 0; - spec->en_3kpull_low = true; - - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; - spec->shutup = alc_default_shutup; - spec->init_hook = alc_default_init; - - switch (codec->core.vendor_id) { - case 0x10ec0269: - spec->codec_variant = ALC269_TYPE_ALC269VA; - switch (alc_get_coef0(codec) & 0x00f0) { - case 0x0010: - if (codec->bus->pci && - codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) - err = alc_codec_rename(codec, "ALC271X"); - spec->codec_variant = ALC269_TYPE_ALC269VB; - break; - case 0x0020: - if (codec->bus->pci && - codec->bus->pci->subsystem_vendor == 0x17aa && - codec->bus->pci->subsystem_device == 0x21f3) - err = alc_codec_rename(codec, "ALC3202"); - spec->codec_variant = ALC269_TYPE_ALC269VC; - break; - case 0x0030: - spec->codec_variant = ALC269_TYPE_ALC269VD; - break; - default: - alc_fix_pll_init(codec, 0x20, 0x04, 15); - } - if (err < 0) - goto error; - spec->shutup = alc269_shutup; - spec->init_hook = alc269_fill_coef; - alc269_fill_coef(codec); - break; - - case 0x10ec0280: - case 0x10ec0290: - spec->codec_variant = ALC269_TYPE_ALC280; - break; - case 0x10ec0282: - spec->codec_variant = ALC269_TYPE_ALC282; - spec->shutup = alc282_shutup; - spec->init_hook = alc282_init; - break; - case 0x10ec0233: - case 0x10ec0283: - spec->codec_variant = ALC269_TYPE_ALC283; - spec->shutup = alc283_shutup; - spec->init_hook = alc283_init; - break; - case 0x10ec0284: - case 0x10ec0292: - spec->codec_variant = ALC269_TYPE_ALC284; - break; - case 0x10ec0293: - spec->codec_variant = ALC269_TYPE_ALC293; - break; - case 0x10ec0286: - case 0x10ec0288: - spec->codec_variant = ALC269_TYPE_ALC286; - break; - case 0x10ec0298: - spec->codec_variant = ALC269_TYPE_ALC298; - break; - case 0x10ec0235: - case 0x10ec0255: - spec->codec_variant = ALC269_TYPE_ALC255; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - break; - case 0x10ec0230: - case 0x10ec0236: - case 0x10ec0256: - case 0x19e58326: - spec->codec_variant = ALC269_TYPE_ALC256; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ - if (codec->core.vendor_id == 0x10ec0236 && - codec->bus->pci->vendor != PCI_VENDOR_ID_AMD) - spec->en_3kpull_low = false; - break; - case 0x10ec0257: - spec->codec_variant = ALC269_TYPE_ALC257; - spec->shutup = alc256_shutup; - spec->init_hook = alc256_init; - spec->gen.mixer_nid = 0; - spec->en_3kpull_low = false; - break; - case 0x10ec0215: - case 0x10ec0245: - case 0x10ec0285: - case 0x10ec0289: - if (alc_get_coef0(codec) & 0x0010) - spec->codec_variant = ALC269_TYPE_ALC245; - else - spec->codec_variant = ALC269_TYPE_ALC215; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; - break; - case 0x10ec0225: - case 0x10ec0295: - case 0x10ec0299: - spec->codec_variant = ALC269_TYPE_ALC225; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; /* no loopback on ALC225, ALC295 and ALC299 */ - break; - case 0x10ec0287: - spec->codec_variant = ALC269_TYPE_ALC287; - spec->shutup = alc225_shutup; - spec->init_hook = alc225_init; - spec->gen.mixer_nid = 0; /* no loopback on ALC287 */ - break; - case 0x10ec0234: - case 0x10ec0274: - case 0x10ec0294: - spec->codec_variant = ALC269_TYPE_ALC294; - spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ - alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ - spec->init_hook = alc294_init; - break; - case 0x10ec0300: - spec->codec_variant = ALC269_TYPE_ALC300; - spec->gen.mixer_nid = 0; /* no loopback on ALC300 */ - break; - case 0x10ec0222: - case 0x10ec0623: - spec->codec_variant = ALC269_TYPE_ALC623; - spec->shutup = alc222_shutup; - spec->init_hook = alc222_init; - break; - case 0x10ec0700: - case 0x10ec0701: - case 0x10ec0703: - case 0x10ec0711: - spec->codec_variant = ALC269_TYPE_ALC700; - spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */ - alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */ - spec->init_hook = alc294_init; - break; - - } - - if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { - spec->has_alc5505_dsp = 1; - spec->init_hook = alc5505_dsp_init; - } - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, alc269_fixup_models, - alc269_fixup_tbl, alc269_fixups); - /* FIXME: both TX300 and ROG Strix G17 have the same SSID, and - * the quirk breaks the latter (bko#214101). - * Clear the wrong entry. - */ - if (codec->fixup_id == ALC282_FIXUP_ASUS_TX300 && - codec->core.vendor_id == 0x10ec0294) { - codec_dbg(codec, "Clear wrong fixup for ASUS ROG Strix G17\n"); - codec->fixup_id = HDA_FIXUP_ID_NOT_SET; - } - - snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true); - snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false); - snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, - alc269_fixups); - - /* - * Check whether ACPI describes companion amplifiers that require - * component binding - */ - find_cirrus_companion_amps(codec); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - - /* automatic parse from the BIOS config */ - err = alc269_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) { - err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861 - */ - -static int alc861_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; - return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); -} - -/* Pin config fixes */ enum { - ALC861_FIXUP_FSC_AMILO_PI1505, - ALC861_FIXUP_AMP_VREF_0F, - ALC861_FIXUP_NO_JACK_DETECT, - ALC861_FIXUP_ASUS_A6RP, - ALC660_FIXUP_ASUS_W7J, + ALC269_FIXUP_GPIO2, + ALC269_FIXUP_SONY_VAIO, + ALC275_FIXUP_SONY_VAIO_GPIO2, + ALC269_FIXUP_DELL_M101Z, + ALC269_FIXUP_SKU_IGNORE, + ALC269_FIXUP_ASUS_G73JW, + ALC269_FIXUP_ASUS_N7601ZM_PINS, + ALC269_FIXUP_ASUS_N7601ZM, + ALC269_FIXUP_LENOVO_EAPD, + ALC275_FIXUP_SONY_HWEQ, + ALC275_FIXUP_SONY_DISABLE_AAMIX, + ALC271_FIXUP_DMIC, + ALC269_FIXUP_PCM_44K, + ALC269_FIXUP_STEREO_DMIC, + ALC269_FIXUP_HEADSET_MIC, + ALC269_FIXUP_QUANTA_MUTE, + ALC269_FIXUP_LIFEBOOK, + ALC269_FIXUP_LIFEBOOK_EXTMIC, + ALC269_FIXUP_LIFEBOOK_HP_PIN, + ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT, + ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, + ALC269_FIXUP_AMIC, + ALC269_FIXUP_DMIC, + ALC269VB_FIXUP_AMIC, + ALC269VB_FIXUP_DMIC, + ALC269_FIXUP_HP_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC269_FIXUP_HP_MUTE_LED_MIC2, + ALC269_FIXUP_HP_MUTE_LED_MIC3, + ALC269_FIXUP_HP_GPIO_LED, + ALC269_FIXUP_HP_GPIO_MIC1_LED, + ALC269_FIXUP_HP_LINE1_MIC1_LED, + ALC269_FIXUP_INV_DMIC, + ALC269_FIXUP_LENOVO_DOCK, + ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, + ALC269_FIXUP_NO_SHUTUP, + ALC286_FIXUP_SONY_MIC_NO_PRESENCE, + ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, + ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, + ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, + ALC269_FIXUP_HEADSET_MODE, + ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC269_FIXUP_ASPIRE_HEADSET_MIC, + ALC269_FIXUP_ASUS_X101_FUNC, + ALC269_FIXUP_ASUS_X101_VERB, + ALC269_FIXUP_ASUS_X101, + ALC271_FIXUP_AMIC_MIC2, + ALC271_FIXUP_HP_GATE_MIC_JACK, + ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, + ALC269_FIXUP_ACER_AC700, + ALC269_FIXUP_LIMIT_INT_MIC_BOOST, + ALC269VB_FIXUP_ASUS_ZENBOOK, + ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, + ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED, + ALC269VB_FIXUP_ORDISSIMO_EVE2, + ALC283_FIXUP_CHROME_BOOK, + ALC283_FIXUP_SENSE_COMBO_JACK, + ALC282_FIXUP_ASUS_TX300, + ALC283_FIXUP_INT_MIC, + ALC290_FIXUP_MONO_SPEAKERS, + ALC290_FIXUP_MONO_SPEAKERS_HSJACK, + ALC290_FIXUP_SUBWOOFER, + ALC290_FIXUP_SUBWOOFER_HSJACK, + ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, + ALC269_FIXUP_THINKPAD_ACPI, + ALC269_FIXUP_LENOVO_XPAD_ACPI, + ALC269_FIXUP_DMIC_THINKPAD_ACPI, + ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, + ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, + ALC255_FIXUP_ACER_MIC_NO_PRESENCE, + ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, + ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC255_FIXUP_HEADSET_MODE, + ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_FIXUP_TPT440_DOCK, + ALC292_FIXUP_TPT440, + ALC283_FIXUP_HEADSET_MIC, + ALC255_FIXUP_MIC_MUTE_LED, + ALC282_FIXUP_ASPIRE_V5_PINS, + ALC269VB_FIXUP_ASPIRE_E1_COEF, + ALC280_FIXUP_HP_GPIO4, + ALC286_FIXUP_HP_GPIO_LED, + ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, + ALC280_FIXUP_HP_DOCK_PINS, + ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, + ALC280_FIXUP_HP_9480M, + ALC245_FIXUP_HP_X360_AMP, + ALC285_FIXUP_HP_SPECTRE_X360_EB1, + ALC285_FIXUP_HP_SPECTRE_X360_DF1, + ALC285_FIXUP_HP_ENVY_X360, + ALC288_FIXUP_DELL_HEADSET_MODE, + ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC288_FIXUP_DELL_XPS_13, + ALC288_FIXUP_DISABLE_AAMIX, + ALC292_FIXUP_DELL_E7X_AAMIX, + ALC292_FIXUP_DELL_E7X, + ALC292_FIXUP_DISABLE_AAMIX, + ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, + ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, + ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, + ALC275_FIXUP_DELL_XPS, + ALC293_FIXUP_LENOVO_SPK_NOISE, + ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, + ALC255_FIXUP_DELL_SPK_NOISE, + ALC225_FIXUP_DISABLE_MIC_VREF, + ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC295_FIXUP_DISABLE_DAC3, + ALC285_FIXUP_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_HEADSET_MIC, + ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, + ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_I2C_HEADSET_MIC, + ALC280_FIXUP_HP_HEADSET_MIC, + ALC221_FIXUP_HP_FRONT_MIC, + ALC292_FIXUP_TPT460, + ALC298_FIXUP_SPK_VOLUME, + ALC298_FIXUP_LENOVO_SPK_VOLUME, + ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, + ALC269_FIXUP_ATIV_BOOK_8, + ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE, + ALC221_FIXUP_HP_MIC_NO_PRESENCE, + ALC256_FIXUP_ASUS_HEADSET_MODE, + ALC256_FIXUP_ASUS_MIC, + ALC256_FIXUP_ASUS_AIO_GPIO2, + ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, + ALC233_FIXUP_LENOVO_MULTI_CODECS, + ALC233_FIXUP_ACER_HEADSET_MIC, + ALC294_FIXUP_LENOVO_MIC_LOCATION, + ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, + ALC225_FIXUP_S3_POP_NOISE, + ALC700_FIXUP_INTEL_REFERENCE, + ALC274_FIXUP_DELL_BIND_DACS, + ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + ALC298_FIXUP_TPT470_DOCK_FIX, + ALC298_FIXUP_TPT470_DOCK, + ALC255_FIXUP_DUMMY_LINEOUT_VERB, + ALC255_FIXUP_DELL_HEADSET_MIC, + ALC256_FIXUP_HUAWEI_MACH_WX9_PINS, + ALC298_FIXUP_HUAWEI_MBX_STEREO, + ALC295_FIXUP_HP_X360, + ALC221_FIXUP_HP_HEADSET_MIC, + ALC285_FIXUP_LENOVO_HEADPHONE_NOISE, + ALC295_FIXUP_HP_AUTO_MUTE, + ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, + ALC294_FIXUP_ASUS_MIC, + ALC294_FIXUP_ASUS_HEADSET_MIC, + ALC294_FIXUP_ASUS_SPK, + ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE, + ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, + ALC255_FIXUP_ACER_HEADSET_MIC, + ALC295_FIXUP_CHROME_BOOK, + ALC225_FIXUP_HEADSET_JACK, + ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE, + ALC225_FIXUP_WYSE_AUTO_MUTE, + ALC225_FIXUP_WYSE_DISABLE_MIC_VREF, + ALC286_FIXUP_ACER_AIO_HEADSET_MIC, + ALC256_FIXUP_ASUS_HEADSET_MIC, + ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC255_FIXUP_PREDATOR_SUBWOOFER, + ALC299_FIXUP_PREDATOR_SPK, + ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, + ALC289_FIXUP_DELL_SPK1, + ALC289_FIXUP_DELL_SPK2, + ALC289_FIXUP_DUAL_SPK, + ALC289_FIXUP_RTK_AMP_DUAL_SPK, + ALC294_FIXUP_SPK2_TO_DAC1, + ALC294_FIXUP_ASUS_DUAL_SPK, + ALC285_FIXUP_THINKPAD_X1_GEN7, + ALC285_FIXUP_THINKPAD_HEADSET_JACK, + ALC294_FIXUP_ASUS_ALLY, + ALC294_FIXUP_ASUS_ALLY_PINS, + ALC294_FIXUP_ASUS_ALLY_VERBS, + ALC294_FIXUP_ASUS_ALLY_SPEAKER, + ALC294_FIXUP_ASUS_HPE, + ALC294_FIXUP_ASUS_COEF_1B, + ALC294_FIXUP_ASUS_GX502_HP, + ALC294_FIXUP_ASUS_GX502_PINS, + ALC294_FIXUP_ASUS_GX502_VERBS, + ALC294_FIXUP_ASUS_GU502_HP, + ALC294_FIXUP_ASUS_GU502_PINS, + ALC294_FIXUP_ASUS_GU502_VERBS, + ALC294_FIXUP_ASUS_G513_PINS, + ALC285_FIXUP_ASUS_G533Z_PINS, + ALC285_FIXUP_HP_GPIO_LED, + ALC285_FIXUP_HP_MUTE_LED, + ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED, + ALC285_FIXUP_HP_BEEP_MICMUTE_LED, + ALC236_FIXUP_HP_MUTE_LED_COEFBIT2, + ALC236_FIXUP_HP_GPIO_LED, + ALC236_FIXUP_HP_MUTE_LED, + ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, + ALC236_FIXUP_LENOVO_INV_DMIC, + ALC298_FIXUP_SAMSUNG_AMP, + ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, + ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, + ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, + ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, + ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS, + ALC269VC_FIXUP_ACER_HEADSET_MIC, + ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, + ALC289_FIXUP_ASUS_GA401, + ALC289_FIXUP_ASUS_GA502, + ALC256_FIXUP_ACER_MIC_NO_PRESENCE, + ALC285_FIXUP_HP_GPIO_AMP_INIT, + ALC269_FIXUP_CZC_B20, + ALC269_FIXUP_CZC_TMI, + ALC269_FIXUP_CZC_L101, + ALC269_FIXUP_LEMOTE_A1802, + ALC269_FIXUP_LEMOTE_A190X, + ALC256_FIXUP_INTEL_NUC8_RUGGED, + ALC233_FIXUP_INTEL_NUC8_DMIC, + ALC233_FIXUP_INTEL_NUC8_BOOST, + ALC256_FIXUP_INTEL_NUC10, + ALC255_FIXUP_XIAOMI_HEADSET_MIC, + ALC274_FIXUP_HP_MIC, + ALC274_FIXUP_HP_HEADSET_MIC, + ALC274_FIXUP_HP_ENVY_GPIO, + ALC274_FIXUP_ASUS_ZEN_AIO_27, + ALC256_FIXUP_ASUS_HPE, + ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, + ALC287_FIXUP_HP_GPIO_LED, + ALC256_FIXUP_HP_HEADSET_MIC, + ALC245_FIXUP_HP_GPIO_LED, + ALC236_FIXUP_DELL_AIO_HEADSET_MIC, + ALC282_FIXUP_ACER_DISABLE_LINEOUT, + ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST, + ALC256_FIXUP_ACER_HEADSET_MIC, + ALC285_FIXUP_IDEAPAD_S740_COEF, + ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST, + ALC295_FIXUP_ASUS_DACS, + ALC295_FIXUP_HP_OMEN, + ALC285_FIXUP_HP_SPECTRE_X360, + ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, + ALC623_FIXUP_LENOVO_THINKSTATION_P340, + ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, + ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST, + ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS, + ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, + ALC287_FIXUP_YOGA7_14ITL_SPEAKERS, + ALC298_FIXUP_LENOVO_C940_DUET7, + ALC287_FIXUP_13S_GEN2_SPEAKERS, + ALC256_FIXUP_SET_COEF_DEFAULTS, + ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, + ALC233_FIXUP_NO_AUDIO_JACK, + ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, + ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, + ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, + ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED, + ALC287_FIXUP_CS35L41_I2C_4, + ALC245_FIXUP_CS35L41_SPI_1, + ALC245_FIXUP_CS35L41_SPI_2, + ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED, + ALC245_FIXUP_CS35L41_SPI_4, + ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED, + ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, + ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE, + ALC287_FIXUP_LEGION_16ITHG6, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, + ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN, + ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS, + ALC236_FIXUP_DELL_DUAL_CODECS, + ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, + ALC287_FIXUP_TAS2781_I2C, + ALC245_FIXUP_TAS2781_SPI_2, + ALC287_FIXUP_TXNW2781_I2C, + ALC287_FIXUP_YOGA7_14ARB7_I2C, + ALC245_FIXUP_HP_MUTE_LED_COEFBIT, + ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT, + ALC245_FIXUP_HP_X360_MUTE_LEDS, + ALC287_FIXUP_THINKPAD_I2S_SPK, + ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, + ALC2XX_FIXUP_HEADSET_MIC, + ALC289_FIXUP_DELL_CS35L41_SPI_2, + ALC294_FIXUP_CS35L41_I2C_2, + ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, + ALC256_FIXUP_HEADPHONE_AMP_VOL, + ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, + ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX, + ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A, + ALC285_FIXUP_ASUS_GA403U, + ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC, + ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1, + ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, + ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1, + ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, + ALC256_FIXUP_CHROME_BOOK, + ALC245_FIXUP_CLEVO_NOISY_MIC, + ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, + ALC233_FIXUP_MEDION_MTL_SPK, + ALC294_FIXUP_BASS_SPEAKER_15, + ALC283_FIXUP_DELL_HP_RESUME, + ALC294_FIXUP_ASUS_CS35L41_SPI_2, + ALC274_FIXUP_HP_AIO_BIND_DACS, + ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, + ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, + ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, + ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, }; -/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ -static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* A special fixup for Lenovo C940 and Yoga Duet 7; + * both have the very same PCI SSID, and we need to apply different fixups + * depending on the codec ID + */ +static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) { - struct alc_spec *spec = codec->spec; - unsigned int val; - - if (action != HDA_FIXUP_ACT_INIT) - return; - val = snd_hda_codec_get_pin_target(codec, 0x0f); - if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) - val |= AC_PINCTL_IN_EN; - val |= AC_PINCTL_VREF_50; - snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->gen.keep_vref_in_automute = 1; -} + int id; -/* suppress the jack-detection */ -static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - codec->no_jack_detect = 1; + if (codec->core.vendor_id == 0x10ec0298) + id = ALC298_FIXUP_LENOVO_SPK_VOLUME; /* C940 */ + else + id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* Duet 7 */ + __snd_hda_apply_fixup(codec, id, action, 0); } -static const struct hda_fixup alc861_fixups[] = { - [ALC861_FIXUP_FSC_AMILO_PI1505] = { +static const struct hda_fixup alc269_fixups[] = { + [ALC269_FIXUP_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, + }, + [ALC269_FIXUP_SONY_VAIO] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + {0x19, PIN_VREFGRD}, + {} + } + }, + [ALC275_FIXUP_SONY_VAIO_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc275_fixup_gpio4_off, + .chained = true, + .chain_id = ALC269_FIXUP_SONY_VAIO + }, + [ALC269_FIXUP_DELL_M101Z] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 13}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4040}, + {} + } + }, + [ALC269_FIXUP_SKU_IGNORE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, + }, + [ALC269_FIXUP_ASUS_G73JW] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x99130111 }, /* subwoofer */ + { } + } + }, + [ALC269_FIXUP_ASUS_N7601ZM_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03A11050 }, + { 0x1a, 0x03A11C30 }, + { 0x21, 0x03211420 }, + { } + } + }, + [ALC269_FIXUP_ASUS_N7601ZM] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x20, AC_VERB_SET_COEF_INDEX, 0x62}, + {0x20, AC_VERB_SET_PROC_COEF, 0xa007}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x10}, + {0x20, AC_VERB_SET_PROC_COEF, 0x8420}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x0f}, + {0x20, AC_VERB_SET_PROC_COEF, 0x7774}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_ASUS_N7601ZM_PINS, + }, + [ALC269_FIXUP_LENOVO_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC275_FIXUP_SONY_HWEQ] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hweq, + .chained = true, + .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 + }, + [ALC275_FIXUP_SONY_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_SONY_VAIO + }, + [ALC271_FIXUP_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc271_fixup_dmic, + }, + [ALC269_FIXUP_PCM_44K] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pcm_44k, + .chained = true, + .chain_id = ALC269_FIXUP_QUANTA_MUTE + }, + [ALC269_FIXUP_STEREO_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_stereo_dmic, + }, + [ALC269_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mic, + }, + [ALC269_FIXUP_QUANTA_MUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_quanta_mute, + }, + [ALC269_FIXUP_LIFEBOOK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x2101103f }, /* dock line-out */ + { 0x1b, 0x23a11040 }, /* dock mic-in */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_QUANTA_MUTE + }, + [ALC269_FIXUP_LIFEBOOK_EXTMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1903c }, /* headset mic, with jack detect */ + { } + }, + }, + [ALC269_FIXUP_LIFEBOOK_HP_PIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x21, 0x0221102f }, /* HP out */ + { } + }, + }, + [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pincfg_no_hp_to_lineout, + }, + [ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pincfg_U7x7_headset_mic, + }, + [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */ + { 0x1b, 0x90170152 }, /* use as internal speaker (back) */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC269VC_FIXUP_INFINIX_Y4_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170150 }, /* use as internal speaker */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03a19020 }, /* headset mic */ + { 0x1b, 0x90170150 }, /* speaker */ + { } + }, + }, + [ALC269_FIXUP_AMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121401f }, /* HP out */ + { 0x18, 0x01a19c20 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { } + }, + }, + [ALC269_FIXUP_DMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x99a3092f }, /* int-mic */ + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121401f }, /* HP out */ + { 0x18, 0x01a19c20 }, /* mic */ + { } + }, + }, + [ALC269VB_FIXUP_AMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19c20 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + }, + [ALC269VB_FIXUP_DMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x99a3092f }, /* int-mic */ + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19c20 }, /* mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + }, + [ALC269_FIXUP_HP_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led, + }, + [ALC269_FIXUP_HP_MUTE_LED_MIC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led_mic1, + }, + [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led_mic2, + }, + [ALC269_FIXUP_HP_MUTE_LED_MIC3] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led_mic3, + .chained = true, + .chain_id = ALC295_FIXUP_HP_AUTO_MUTE + }, + [ALC269_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_gpio_led, + }, + [ALC269_FIXUP_HP_GPIO_MIC1_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_gpio_mic1_led, + }, + [ALC269_FIXUP_HP_LINE1_MIC1_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_line1_mic1_led, + }, + [ALC269_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC269_FIXUP_NO_SHUTUP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_shutup, + }, + [ALC269_FIXUP_LENOVO_DOCK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x23a11040 }, /* dock mic */ + { 0x1b, 0x2121103f }, /* dock headphone */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT + }, + [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_LENOVO_DOCK, + }, + [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pincfg_no_hp_to_lineout, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x21014020 }, /* dock line out */ + { 0x19, 0x21a19030 }, /* dock mic */ + { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC269_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, + [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_no_hp_mic, + }, + [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE, + }, + [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x90a60130}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x18, 0x411111f0}, + {0x19, 0x04a11040}, + {0x1a, 0x411111f0}, + {0x1b, 0x90170112}, + {0x1d, 0x40759a05}, + {0x1e, 0x411111f0}, + {0x21, 0x04211020}, + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, + [ALC298_FIXUP_HUAWEI_MBX_STEREO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_huawei_mbx_stereo, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, + [ALC269_FIXUP_ASUS_X101_FUNC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_x101_headset_mic, + }, + [ALC269_FIXUP_ASUS_X101_VERB] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0310}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_ASUS_X101_FUNC + }, + [ALC269_FIXUP_ASUS_X101] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x04a1182c }, /* Headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_ASUS_X101_VERB + }, + [ALC271_FIXUP_AMIC_MIC2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x19, 0x01a19c20 }, /* mic */ + { 0x1b, 0x99a7012f }, /* int-mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + }, + [ALC271_FIXUP_HP_GATE_MIC_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc271_hp_gate_mic_jack, + .chained = true, + .chain_id = ALC271_FIXUP_AMIC_MIC2, + }, + [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK, + }, + [ALC269_FIXUP_ACER_AC700] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x99a3092f }, /* int-mic */ + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x03a11c20 }, /* mic */ + { 0x1e, 0x0346101e }, /* SPDIF1 */ + { 0x21, 0x0321101f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC271_FIXUP_DMIC, + }, + [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC269VB_FIXUP_ASUS_ZENBOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269VB_FIXUP_DMIC, + }, + [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* class-D output amp +5dB */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 }, + {} + }, + .chained = true, + .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK, + }, + [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a110f0 }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1, + }, + [ALC269VB_FIXUP_ORDISSIMO_EVE2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x99a3092f }, /* int-mic */ + { 0x18, 0x03a11d20 }, /* mic */ + { 0x19, 0x411111f0 }, /* Unused bogus pin */ + { } + }, + }, + [ALC283_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_chromebook, + }, + [ALC283_FIXUP_SENSE_COMBO_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_sense_combo_jack, + .chained = true, + .chain_id = ALC283_FIXUP_CHROME_BOOK, + }, + [ALC282_FIXUP_ASUS_TX300] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc282_fixup_asus_tx300, + }, + [ALC283_FIXUP_INT_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x20, AC_VERB_SET_COEF_INDEX, 0x1a}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0011}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC290_FIXUP_SUBWOOFER_HSJACK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170112 }, /* subwoofer */ + { } + }, + .chained = true, + .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, + }, + [ALC290_FIXUP_SUBWOOFER] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170112 }, /* subwoofer */ + { } + }, + .chained = true, + .chain_id = ALC290_FIXUP_MONO_SPEAKERS, + }, + [ALC290_FIXUP_MONO_SPEAKERS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc290_fixup_mono_speakers, + }, + [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc290_fixup_mono_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, + }, + [ALC269_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_thinkpad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_SKU_IGNORE, + }, + [ALC269_FIXUP_LENOVO_XPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_ideapad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE + }, + [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE + }, + [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE + }, + [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC255_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc255, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, + [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc255_no_hp_mic, + }, + [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC292_FIXUP_TPT440_DOCK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt440_dock, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, + [ALC292_FIXUP_TPT440] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC292_FIXUP_TPT440_DOCK, + }, + [ALC283_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x04a110f0 }, + { }, + }, + }, + [ALC255_FIXUP_MIC_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_micmute_led, + }, + [ALC282_FIXUP_ASPIRE_V5_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x90a60130 }, + { 0x14, 0x90170110 }, + { 0x17, 0x40000008 }, + { 0x18, 0x411111f0 }, + { 0x19, 0x01a1913c }, + { 0x1a, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1d, 0x40f89b2d }, + { 0x1e, 0x411111f0 }, + { 0x21, 0x0321101f }, + { }, + }, + }, + [ALC269VB_FIXUP_ASPIRE_E1_COEF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269vb_fixup_aspire_e1_coef, + }, + [ALC280_FIXUP_HP_GPIO4] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc280_fixup_hp_gpio4, + }, + [ALC286_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc286_fixup_hp_gpio_led, + }, + [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc280_fixup_hp_gpio2_mic_hotkey, + }, + [ALC280_FIXUP_HP_DOCK_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x21011020 }, /* line-out */ + { 0x1a, 0x01a1903c }, /* headset mic */ + { 0x18, 0x2181103f }, /* line-in */ + { }, + }, + .chained = true, + .chain_id = ALC280_FIXUP_HP_GPIO4 + }, + [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x21011020 }, /* line-out */ + { 0x18, 0x2181103f }, /* line-in */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED + }, + [ALC280_FIXUP_HP_9480M] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc280_fixup_hp_9480m, + }, + [ALC245_FIXUP_HP_X360_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_x360_amp, + .chained = true, + .chain_id = ALC245_FIXUP_HP_GPIO_LED + }, + [ALC288_FIXUP_DELL_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_dell_alc288, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED + }, + [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE + }, + [ALC288_FIXUP_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC288_FIXUP_DELL_XPS_13] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC288_FIXUP_DISABLE_AAMIX + }, + [ALC292_FIXUP_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE + }, + [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC292_FIXUP_DELL_E7X_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC292_FIXUP_DISABLE_AAMIX + }, + [ALC292_FIXUP_DELL_E7X] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_micmute_led, + /* micmute fixup must be applied at last */ + .chained_before = true, + .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX, + }, + [ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* headset mic w/o jack detect */ + { } + }, + .chained_before = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE, + }, + [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC275_FIXUP_DELL_XPS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x1f}, + {0x20, AC_VERB_SET_PROC_COEF, 0x00c0}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x30}, + {0x20, AC_VERB_SET_PROC_COEF, 0x00b1}, + {} + } + }, + [ALC293_FIXUP_LENOVO_SPK_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_line2_mic_hotkey, + }, + [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_low_en_micmute_led, + }, + [ALC233_FIXUP_INTEL_NUC8_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + .chained = true, + .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST, + }, + [ALC233_FIXUP_INTEL_NUC8_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost + }, + [ALC255_FIXUP_DELL_SPK_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC225_FIXUP_DISABLE_MIC_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_mic_vref, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Disable pass-through path for FRONT 14h */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, + {} + }, + .chained = true, + .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF + }, + [ALC280_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC, + }, + [ALC221_FIXUP_HP_FRONT_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a19020 }, /* Front Mic */ + { } + }, + }, + [ALC292_FIXUP_TPT460] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt440_dock, + .chained = true, + .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE, + }, + [ALC298_FIXUP_SPK_VOLUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_speaker_volume, + .chained = true, + .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, + }, + [ALC298_FIXUP_LENOVO_SPK_VOLUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_speaker_volume, + }, + [ALC295_FIXUP_DISABLE_DAC3] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_disable_dac3, + }, + [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC245_FIXUP_CS35L41_SPI_2 + }, + [ALC285_FIXUP_ASUS_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1 + }, + [ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170120 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_HEADSET_MIC + }, + [ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2 + }, + [ALC285_FIXUP_ASUS_I2C_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1 + }, + [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170151 }, + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC269_FIXUP_ATIV_BOOK_8] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC269_FIXUP_NO_SHUTUP + }, + [ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01813030 }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC256_FIXUP_ASUS_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode, + }, + [ALC256_FIXUP_ASUS_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x13, 0x90a60160 }, /* use as internal mic */ + { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, + [ALC256_FIXUP_ASUS_AIO_GPIO2] = { + .type = HDA_FIXUP_FUNC, + /* Set up GPIO2 for the speaker amp */ + .v.func = alc_fixup_gpio4, + }, + [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x40}, + {0x20, AC_VERB_SET_PROC_COEF, 0x8800}, + {} + }, + .chained = true, + .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE + }, + [ALC233_FIXUP_LENOVO_MULTI_CODECS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_alc662_fixup_lenovo_dual_codecs, + .chained = true, + .chain_id = ALC269_FIXUP_GPIO2 + }, + [ALC233_FIXUP_ACER_HEADSET_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, + { } + }, + .chained = true, + .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE + }, + [ALC294_FIXUP_LENOVO_MIC_LOCATION] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* Change the mic location from front to right, otherwise there are + two front mics with the same name, pulseaudio can't handle them. + This is just a temporary workaround, after applying this fixup, + there will be one "Front Mic" and one "Mic" in this machine. + */ + { 0x1a, 0x04a19040 }, + { } + }, + }, + [ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x0101102f }, /* Rear Headset HP */ + { 0x19, 0x02a1913c }, /* use as Front headset mic, without its own jack detect */ + { 0x1a, 0x01a19030 }, /* Rear Headset MIC */ + { 0x1b, 0x02011020 }, + { } + }, + .chained = true, + .chain_id = ALC225_FIXUP_S3_POP_NOISE + }, + [ALC225_FIXUP_S3_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc225_fixup_s3_pop_noise, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC700_FIXUP_INTEL_REFERENCE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enables internal speaker */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x45}, + {0x20, AC_VERB_SET_PROC_COEF, 0x5289}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x4A}, + {0x20, AC_VERB_SET_PROC_COEF, 0x001b}, + {0x58, AC_VERB_SET_COEF_INDEX, 0x00}, + {0x58, AC_VERB_SET_PROC_COEF, 0x3888}, + {0x20, AC_VERB_SET_COEF_INDEX, 0x6f}, + {0x20, AC_VERB_SET_PROC_COEF, 0x2c0b}, + {} + } + }, + [ALC274_FIXUP_DELL_BIND_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_bind_dacs, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x0401102f }, + { } + }, + .chained = true, + .chain_id = ALC274_FIXUP_DELL_BIND_DACS + }, + [ALC298_FIXUP_TPT470_DOCK_FIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt470_dock, + .chained = true, + .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE + }, + [ALC298_FIXUP_TPT470_DOCK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt470_dacs, + .chained = true, + .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX + }, + [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0201101f }, + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC255_FIXUP_DELL_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC295_FIXUP_HP_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_top_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 + }, + [ALC221_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x0181313f}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_invalidate_dacs, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC295_FIXUP_HP_AUTO_MUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + }, + [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC294_FIXUP_ASUS_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x13, 0x90a60160 }, /* use as internal mic */ + { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC294_FIXUP_ASUS_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1103c }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC294_FIXUP_ASUS_SPK] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set EAPD high */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC + }, + [ALC295_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_chromebook, + .chained = true, + .chain_id = ALC225_FIXUP_HEADSET_JACK + }, + [ALC225_FIXUP_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + }, + [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Disable PCBEEP-IN passthrough */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE + }, + [ALC255_FIXUP_ACER_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11130 }, + { 0x1a, 0x90a60140 }, /* use as internal mic */ + { } + }, + .chained = true, + .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x01011020 }, /* Rear Line out */ + { 0x19, 0x01a1913c }, /* use as Front headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC225_FIXUP_WYSE_AUTO_MUTE + }, + [ALC225_FIXUP_WYSE_AUTO_MUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC225_FIXUP_WYSE_DISABLE_MIC_VREF + }, + [ALC225_FIXUP_WYSE_DISABLE_MIC_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_mic_vref, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x4f }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5029 }, + { } + }, + .chained = true, + .chain_id = ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE + }, + [ALC256_FIXUP_ASUS_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11020 }, /* headset mic with jack detect */ + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, + [ALC256_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, + [ALC255_FIXUP_PREDATOR_SUBWOOFER] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */ + { 0x1b, 0x90170152 } /* use as internal speaker (back) */ + } + }, + [ALC299_FIXUP_PREDATOR_SPK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x21, 0x90170150 }, /* use as headset mic, without its own jack detect */ + { } + } + }, + [ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + .chained = true, + .chain_id = ALC255_FIXUP_PREDATOR_SUBWOOFER + }, + [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x04a11040 }, + { 0x21, 0x04211020 }, + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, + [ALC289_FIXUP_DELL_SPK1] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170140 }, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE + }, + [ALC289_FIXUP_DELL_SPK2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170130 }, /* bass spk */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE + }, + [ALC289_FIXUP_DUAL_SPK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC289_FIXUP_DELL_SPK2 + }, + [ALC289_FIXUP_RTK_AMP_DUAL_SPK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC289_FIXUP_DELL_SPK1 + }, + [ALC294_FIXUP_SPK2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC + }, + [ALC294_FIXUP_ASUS_DUAL_SPK] = { + .type = HDA_FIXUP_FUNC, + /* The GPIO must be pulled to initialize the AMP */ + .v.func = alc_fixup_gpio4, + .chained = true, + .chain_id = ALC294_FIXUP_SPK2_TO_DAC1 + }, + [ALC294_FIXUP_ASUS_ALLY] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS + }, + [ALC294_FIXUP_ASUS_ALLY_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, + { 0x1a, 0x03a11c30 }, + { 0x21, 0x03211420 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_ALLY_VERBS + }, + [ALC294_FIXUP_ASUS_ALLY_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x46 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0004 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x47 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xa47a }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0049}, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x201b }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4278}, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER + }, + [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + }, + [ALC285_FIXUP_THINKPAD_X1_GEN7] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_thinkpad_x1_gen7, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7 + }, + [ALC294_FIXUP_ASUS_HPE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set EAPD high */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC + }, + [ALC294_FIXUP_ASUS_GX502_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* front HP mic */ + { 0x1a, 0x01a11830 }, /* rear external mic */ + { 0x21, 0x03211020 }, /* front HP out */ + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_GX502_VERBS + }, + [ALC294_FIXUP_ASUS_GX502_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* set 0x15 to HP-OUT ctrl */ + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* unmute the 0x15 amp */ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_GX502_HP + }, + [ALC294_FIXUP_ASUS_GX502_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc294_fixup_gx502_hp, + }, + [ALC294_FIXUP_ASUS_GU502_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a11050 }, /* rear HP mic */ + { 0x1a, 0x01a11830 }, /* rear external mic */ + { 0x21, 0x012110f0 }, /* rear HP out */ + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_GU502_VERBS + }, + [ALC294_FIXUP_ASUS_GU502_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* set 0x15 to HP-OUT ctrl */ + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, + /* unmute the 0x15 amp */ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 }, + /* set 0x1b to HP-OUT */ + { 0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_GU502_HP + }, + [ALC294_FIXUP_ASUS_GU502_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc294_fixup_gu502_hp, + }, + [ALC294_FIXUP_ASUS_G513_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* front HP mic */ + { 0x1a, 0x03a11c30 }, /* rear external mic */ + { 0x21, 0x03211420 }, /* front HP out */ + { } + }, + }, + [ALC285_FIXUP_ASUS_G533Z_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170152 }, /* Speaker Surround Playback Switch */ + { 0x19, 0x03a19020 }, /* Mic Boost Volume */ + { 0x1a, 0x03a11c30 }, /* Mic Boost Volume */ + { 0x1e, 0x90170151 }, /* Rear jack, IN OUT EAPD Detect */ + { 0x21, 0x03211420 }, + { } + }, + }, + [ALC294_FIXUP_ASUS_COEF_1B] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set bit 10 to correct noisy output after reboot from + * Windows 10 (due to pop noise reduction?) + */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b }, + { } + }, + .chained = true, + .chain_id = ALC289_FIXUP_ASUS_GA401, + }, + [ALC285_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_gpio_led, + }, + [ALC285_FIXUP_HP_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_mute_led, + }, + [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_spectre_x360_mute_led, + }, + [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_beep, + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, + }, + [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led_coefbit2, + }, + [ALC236_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_gpio_led, + }, + [ALC236_FIXUP_HP_MUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led, + }, + [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc236_fixup_hp_mute_led_micmute_vref, + }, + [ALC236_FIXUP_LENOVO_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + .chained = true, + .chain_id = ALC283_FIXUP_INT_MIC, + }, + [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_mute_led_coefbit11, + }, + [ALC298_FIXUP_SAMSUNG_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_samsung_amp, + .chained = true, + .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET + }, + [ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_samsung_amp_v2_2_amps + }, + [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_samsung_amp_v2_4_amps + }, + [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 }, + { } + }, + }, + [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x08}, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf}, + { } + }, + }, + [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x0b, 0x0221101f }, /* HP */ - { 0x0f, 0x90170310 }, /* speaker */ + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ { } - } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE }, - [ALC861_FIXUP_AMP_VREF_0F] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, + [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90100120 }, /* use as internal speaker */ + { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01011020 }, /* use as line out */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC269VC_FIXUP_ACER_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x02a11030 }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC }, - [ALC861_FIXUP_NO_JACK_DETECT] = { + [ALC289_FIXUP_ASUS_GA401] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, + .v.func = alc289_fixup_asus_ga401, + .chained = true, + .chain_id = ALC289_FIXUP_ASUS_GA502, + }, + [ALC289_FIXUP_ASUS_GA502] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11020 }, /* headset mic with jack detect */ + { } + }, + }, + [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE }, - [ALC861_FIXUP_ASUS_A6RP] = { + [ALC285_FIXUP_HP_GPIO_AMP_INIT] = { .type = HDA_FIXUP_FUNC, - .v.func = alc861_fixup_asus_amp_vref_0f, + .v.func = alc285_fixup_hp_gpio_amp_init, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED + }, + [ALC269_FIXUP_CZC_B20] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x411111f0 }, + { 0x14, 0x90170110 }, /* speaker */ + { 0x15, 0x032f1020 }, /* HP out */ + { 0x17, 0x411111f0 }, + { 0x18, 0x03ab1040 }, /* mic */ + { 0x19, 0xb7a7013f }, + { 0x1a, 0x0181305f }, + { 0x1b, 0x411111f0 }, + { 0x1d, 0x411111f0 }, + { 0x1e, 0x411111f0 }, + { } + }, + .chain_id = ALC269_FIXUP_DMIC, + }, + [ALC269_FIXUP_CZC_TMI] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x4000c000 }, + { 0x14, 0x90170110 }, /* speaker */ + { 0x15, 0x0421401f }, /* HP out */ + { 0x17, 0x411111f0 }, + { 0x18, 0x04a19020 }, /* mic */ + { 0x19, 0x411111f0 }, + { 0x1a, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1d, 0x40448505 }, + { 0x1e, 0x411111f0 }, + { 0x20, 0x8000ffff }, + { } + }, + .chain_id = ALC269_FIXUP_DMIC, + }, + [ALC269_FIXUP_CZC_L101] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x40000000 }, + { 0x14, 0x01014010 }, /* speaker */ + { 0x15, 0x411111f0 }, /* HP out */ + { 0x16, 0x411111f0 }, + { 0x18, 0x01a19020 }, /* mic */ + { 0x19, 0x02a19021 }, + { 0x1a, 0x0181302f }, + { 0x1b, 0x0221401f }, + { 0x1c, 0x411111f0 }, + { 0x1d, 0x4044c601 }, + { 0x1e, 0x411111f0 }, + { } + }, + .chain_id = ALC269_FIXUP_DMIC, + }, + [ALC269_FIXUP_LEMOTE_A1802] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x12, 0x40000000 }, + { 0x14, 0x90170110 }, /* speaker */ + { 0x17, 0x411111f0 }, + { 0x18, 0x03a19040 }, /* mic1 */ + { 0x19, 0x90a70130 }, /* mic2 */ + { 0x1a, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1d, 0x40489d2d }, + { 0x1e, 0x411111f0 }, + { 0x20, 0x0003ffff }, + { 0x21, 0x03214020 }, + { } + }, + .chain_id = ALC269_FIXUP_DMIC, + }, + [ALC269_FIXUP_LEMOTE_A190X] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121401f }, /* HP out */ + { 0x18, 0x01a19c20 }, /* rear mic */ + { 0x19, 0x99a3092f }, /* front mic */ + { 0x1b, 0x0201401f }, /* front lineout */ + { } + }, + .chain_id = ALC269_FIXUP_DMIC, + }, + [ALC256_FIXUP_INTEL_NUC8_RUGGED] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC256_FIXUP_INTEL_NUC10] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, .chained = true, - .chain_id = ALC861_FIXUP_NO_JACK_DETECT, + .chain_id = ALC269_FIXUP_HEADSET_MODE }, - [ALC660_FIXUP_ASUS_W7J] = { + [ALC255_FIXUP_XIAOMI_HEADSET_MIC] = { .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { - /* ASUS W7J needs a magic pin setup on unused NID 0x10 - * for enabling outputs - */ - {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, + { } + }, + .chained = true, + .chain_id = ALC289_FIXUP_ASUS_GA502 + }, + [ALC274_FIXUP_HP_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, { } }, - } -}; - -static const struct hda_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), - SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), - {} -}; - -/* - */ -static int patch_alc861(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x15); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->power_hook = alc_power_eapd; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC861-VD support - * - * Based on ALC882 - * - * In addition, an independent DAC - */ -static int alc861vd_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); -} - -enum { - ALC660VD_FIX_ASUS_GPIO1, - ALC861VD_FIX_DALLAS, -}; - -/* exclude VREF80 */ -static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_override_pin_caps(codec, 0x18, 0x00000734); - snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); - } -} - -/* reset GPIO1 */ -static void alc660vd_fixup_asus_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->gpio_mask |= 0x02; - alc_fixup_gpio(codec, action, 0x01); -} - -static const struct hda_fixup alc861vd_fixups[] = { - [ALC660VD_FIX_ASUS_GPIO1] = { + }, + [ALC274_FIXUP_HP_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc660vd_fixup_asus_gpio1, + .v.func = alc274_fixup_hp_headset_mic, + .chained = true, + .chain_id = ALC274_FIXUP_HP_MIC }, - [ALC861VD_FIX_DALLAS] = { + [ALC274_FIXUP_HP_ENVY_GPIO] = { .type = HDA_FIXUP_FUNC, - .v.func = alc861vd_fixup_dallas, + .v.func = alc274_fixup_hp_envy_gpio, }, -}; - -static const struct hda_quirk alc861vd_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), - SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), - {} -}; - -/* - */ -static int patch_alc861vd(struct hda_codec *codec) -{ - struct alc_spec *spec; - int err; - - err = alc_alloc_spec(codec, 0x0b); - if (err < 0) - return err; - - spec = codec->spec; - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x23; - - spec->shutup = alc_eapd_shutup; - - alc_pre_init(codec); - - snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* automatic parse from the BIOS config */ - err = alc861vd_parse_auto_config(codec); - if (err < 0) - goto error; - - if (!spec->gen.no_analog) { - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - if (err < 0) - goto error; - } - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - alc_free(codec); - return err; -} - -/* - * ALC662 support - * - * ALC662 is almost identical with ALC880 but has cleaner and more flexible - * configuration. Each pin widget can choose any input DACs and a mixer. - * Each ADC is connected from a mixer of all inputs. This makes possible - * 6-channel independent captures. - * - * In addition, an independent DAC for the multi-playback (not used in this - * driver yet). - */ - -/* - * BIOS auto configuration - */ - -static int alc662_parse_auto_config(struct hda_codec *codec) -{ - static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; - static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; - static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; - const hda_nid_t *ssids; - - if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || - codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || - codec->core.vendor_id == 0x10ec0671) - ssids = alc663_ssids; - else - ssids = alc662_ssids; - return alc_parse_auto_config(codec, alc662_ignore, ssids); -} - -static void alc272_fixup_mario(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, - (0x3b << AC_AMPCAP_OFFSET_SHIFT) | - (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT))) - codec_warn(codec, "failed to override amp caps for NID 0x2\n"); -} - -static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { - { .channels = 2, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, - { .channels = 4, - .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, - SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ - { } -}; - -/* override the 2.1 chmap */ -static void alc_fixup_bass_chmap(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_BUILD) { - struct alc_spec *spec = codec->spec; - spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; - } -} - -/* avoid D3 for keeping GPIO up */ -static unsigned int gpio_led_power_filter(struct hda_codec *codec, - hda_nid_t nid, - unsigned int power_state) -{ - struct alc_spec *spec = codec->spec; - if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) - return AC_PWRST_D0; - return power_state; -} - -static void alc662_fixup_led_gpio1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - alc_fixup_hp_gpio_led(codec, action, 0x01, 0); - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 1; - codec->power_filter = gpio_led_power_filter; - } -} - -static void alc662_usi_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - msleep(200); - snd_hda_gen_hp_automute(codec, jack); - - vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; - msleep(100); - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - vref); -} - -static void alc662_fixup_usi_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc662_usi_automute_hook; - } -} - -static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, - struct hda_jack_callback *cb) -{ - /* surround speakers at 0x1b already get muted automatically when - * headphones are plugged in, but we have to mute/unmute the remaining - * channels manually: - * 0x15 - front left/front right - * 0x18 - front center/ LFE - */ - if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { - snd_hda_set_pin_ctl_cache(codec, 0x15, 0); - snd_hda_set_pin_ctl_cache(codec, 0x18, 0); - } else { - snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); - snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); - } -} - -static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - /* Pin 0x1b: shared headphones jack and surround speakers */ - if (!is_jack_detectable(codec, 0x1b)) - return; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_jack_detect_enable_callback(codec, 0x1b, - alc662_aspire_ethos_mute_speakers); - /* subwoofer needs an extra GPIO setting to become audible */ - alc_setup_gpio(codec, 0x02); - break; - case HDA_FIXUP_ACT_INIT: - /* Make sure to start in a correct state, i.e. if - * headphones have been plugged in before powering up the system - */ - alc662_aspire_ethos_mute_speakers(codec, NULL); - break; - } -} - -static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - static const struct hda_pintbl pincfgs[] = { - { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */ - { 0x1b, 0x0181304f }, - { } - }; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.mixer_nid = 0; - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - snd_hda_apply_pincfgs(codec, pincfgs); - break; - case HDA_FIXUP_ACT_INIT: - alc_write_coef_idx(codec, 0x19, 0xa054); - break; - } -} - -static void alc897_hp_automute_hook(struct hda_codec *codec, - struct hda_jack_callback *jack) -{ - struct alc_spec *spec = codec->spec; - int vref; - - snd_hda_gen_hp_automute(codec, jack); - vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP; - snd_hda_set_pin_ctl(codec, 0x1b, vref); -} - -static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.hp_automute_hook = alc897_hp_automute_hook; - spec->no_shutup_pins = 1; - } - if (action == HDA_FIXUP_ACT_PROBE) { - snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100); - } -} - -static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; - spec->gen.hp_automute_hook = alc897_hp_automute_hook; - } -} - -static const struct coef_fw alc668_coefs[] = { - WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), - WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), - WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0), - WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f), - WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001), - WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940), - WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0), - WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418), - WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468), - WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418), - WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00), - WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000), - WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0), - WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480), - WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0), - WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040), - WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697), - WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab), - WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02), - WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6), - {} -}; - -static void alc668_restore_default_value(struct hda_codec *codec) -{ - alc_process_coef_fw(codec, alc668_coefs); -} - -enum { - ALC662_FIXUP_ASPIRE, - ALC662_FIXUP_LED_GPIO1, - ALC662_FIXUP_IDEAPAD, - ALC272_FIXUP_MARIO, - ALC662_FIXUP_CZC_ET26, - ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_SKU_IGNORE, - ALC662_FIXUP_HP_RP5800, - ALC662_FIXUP_ASUS_MODE1, - ALC662_FIXUP_ASUS_MODE2, - ALC662_FIXUP_ASUS_MODE3, - ALC662_FIXUP_ASUS_MODE4, - ALC662_FIXUP_ASUS_MODE5, - ALC662_FIXUP_ASUS_MODE6, - ALC662_FIXUP_ASUS_MODE7, - ALC662_FIXUP_ASUS_MODE8, - ALC662_FIXUP_NO_JACK_DETECT, - ALC662_FIXUP_ZOTAC_Z68, - ALC662_FIXUP_INV_DMIC, - ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - ALC668_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_HEADSET_MODE, - ALC668_FIXUP_HEADSET_MODE, - ALC662_FIXUP_BASS_MODE4_CHMAP, - ALC662_FIXUP_BASS_16, - ALC662_FIXUP_BASS_1A, - ALC662_FIXUP_BASS_CHMAP, - ALC668_FIXUP_AUTO_MUTE, - ALC668_FIXUP_DELL_DISABLE_AAMIX, - ALC668_FIXUP_DELL_XPS13, - ALC662_FIXUP_ASUS_Nx50, - ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, - ALC668_FIXUP_ASUS_Nx51, - ALC668_FIXUP_MIC_COEF, - ALC668_FIXUP_ASUS_G751, - ALC891_FIXUP_HEADSET_MODE, - ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - ALC662_FIXUP_ACER_VERITON, - ALC892_FIXUP_ASROCK_MOBO, - ALC662_FIXUP_USI_FUNC, - ALC662_FIXUP_USI_HEADSET_MODE, - ALC662_FIXUP_LENOVO_MULTI_CODECS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, - ALC671_FIXUP_HP_HEADSET_MIC2, - ALC662_FIXUP_ACER_X2660G_HEADSET_MODE, - ALC662_FIXUP_ACER_NITRO_HEADSET_MODE, - ALC668_FIXUP_ASUS_NO_HEADSET_MIC, - ALC668_FIXUP_HEADSET_MIC, - ALC668_FIXUP_MIC_DET_COEF, - ALC897_FIXUP_LENOVO_HEADSET_MIC, - ALC897_FIXUP_HEADSET_MIC_PIN, - ALC897_FIXUP_HP_HSMIC_VERB, - ALC897_FIXUP_LENOVO_HEADSET_MODE, - ALC897_FIXUP_HEADSET_MIC_PIN2, - ALC897_FIXUP_UNIS_H3C_X500S, - ALC897_FIXUP_HEADSET_MIC_PIN3, -}; - -static const struct hda_fixup alc662_fixups[] = { - [ALC662_FIXUP_ASPIRE] = { + [ALC274_FIXUP_ASUS_ZEN_AIO_27] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x202b }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 }, + {} + }, + .chained = true, + .chain_id = ALC2XX_FIXUP_HEADSET_MIC, + }, + [ALC256_FIXUP_ASUS_HPE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set EAPD high */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 }, + { } + }, + .chained = true, + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC + }, + [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC287_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_hp_gpio_led, + }, + [ALC256_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_hp_headset_mic, + }, + [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_int_mic, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x99130112 }, /* subwoofer */ - { } - } + { 0x1b, 0x411111f0 }, + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE }, - [ALC662_FIXUP_LED_GPIO1] = { + [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = { .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_led_gpio1, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, }, - [ALC662_FIXUP_IDEAPAD] = { + [ALC256_FIXUP_ACER_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x17, 0x99130112 }, /* subwoofer */ + { 0x19, 0x02a1113c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x90a1092f }, /* use as internal mic */ { } }, .chained = true, - .chain_id = ALC662_FIXUP_LED_GPIO1, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, + [ALC285_FIXUP_IDEAPAD_S740_COEF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, }, - [ALC272_FIXUP_MARIO] = { + [ALC295_FIXUP_ASUS_DACS] = { .type = HDA_FIXUP_FUNC, - .v.func = alc272_fixup_mario, + .v.func = alc295_fixup_asus_dacs, }, - [ALC662_FIXUP_CZC_ET26] = { + [ALC295_FIXUP_HP_OMEN] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - {0x12, 0x403cc000}, - {0x14, 0x90170110}, /* speaker */ - {0x15, 0x411111f0}, - {0x16, 0x411111f0}, - {0x18, 0x01a19030}, /* mic */ - {0x19, 0x90a7013f}, /* int-mic */ - {0x1a, 0x01014020}, - {0x1b, 0x0121401f}, - {0x1c, 0x411111f0}, - {0x1d, 0x411111f0}, - {0x1e, 0x40478e35}, + { 0x12, 0xb7a60130 }, + { 0x13, 0x40000000 }, + { 0x14, 0x411111f0 }, + { 0x16, 0x411111f0 }, + { 0x17, 0x90170110 }, + { 0x18, 0x411111f0 }, + { 0x19, 0x02a11030 }, + { 0x1a, 0x411111f0 }, + { 0x1b, 0x04a19030 }, + { 0x1d, 0x40600001 }, + { 0x1e, 0x411111f0 }, + { 0x21, 0x03211020 }, {} }, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC269_FIXUP_HP_LINE1_MIC1_LED, }, - [ALC662_FIXUP_CZC_P10T] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, - {} - } + [ALC285_FIXUP_HP_SPECTRE_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_spectre_x360, }, - [ALC662_FIXUP_SKU_IGNORE] = { + [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_sku_ignore, + .v.func = alc285_fixup_hp_spectre_x360_eb1 + }, + [ALC285_FIXUP_HP_SPECTRE_X360_DF1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_spectre_x360_df1 + }, + [ALC285_FIXUP_HP_ENVY_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_envy_x360, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT, + }, + [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, + }, + [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_shutup, + .chained = true, + .chain_id = ALC283_FIXUP_HEADSET_MIC, }, - [ALC662_FIXUP_HP_RP5800] = { + [ALC255_FIXUP_ACER_HEADPHONE_AND_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x0221201f }, /* HP out */ + { 0x21, 0x03211030 }, /* Change the Headphone location to Left */ { } }, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC255_FIXUP_XIAOMI_HEADSET_MIC }, - [ALC662_FIXUP_ASUS_MODE1] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19c20 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x21, 0x0121401f }, /* HP out */ - { } + [ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF, + }, + [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_ideapad_s740_coef, + .chained = true, + .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + }, + [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_15imhg05_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = { + .type = HDA_FIXUP_VERBS, + //.v.verbs = legion_15imhg05_coefs, + .v.verbs = (const struct hda_verb[]) { + // set left speaker Legion 7i. + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + // set right speaker Legion 7i. + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + {} }, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE, }, - [ALC662_FIXUP_ASUS_MODE2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x18, 0x01a19820 }, /* mic */ - { 0x19, 0x99a3092f }, /* int-mic */ - { 0x1b, 0x0121401f }, /* HP out */ - { } + [ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_15imhg05_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE, + }, + [ALC287_FIXUP_YOGA7_14ITL_SPEAKERS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + // set left speaker Yoga 7i. + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + // set right speaker Yoga 7i. + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + {} + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE, + }, + [ALC298_FIXUP_LENOVO_C940_DUET7] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_lenovo_c940_duet7, + }, + [ALC287_FIXUP_13S_GEN2_SPEAKERS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + {} }, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC269_FIXUP_HEADSET_MODE, }, - [ALC662_FIXUP_ASUS_MODE3] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x01211420 }, /* HP2 */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + [ALC256_FIXUP_SET_COEF_DEFAULTS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_set_coef_defaults, }, - [ALC662_FIXUP_ASUS_MODE4] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x21, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + [ALC245_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_gpio_led, }, - [ALC662_FIXUP_ASUS_MODE5] = { + [ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x0121441f }, /* HP */ - { 0x16, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ + { 0x19, 0x03a11120 }, /* use as headset mic, without its own jack detect */ { } }, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, }, - [ALC662_FIXUP_ASUS_MODE6] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x15, 0x01211420 }, /* HP2 */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x0121441f }, /* HP */ - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + [ALC233_FIXUP_NO_AUDIO_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_no_audio_jack, }, - [ALC662_FIXUP_ASUS_MODE7] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x19, 0x99a3094f }, /* int-mic */ - { 0x1b, 0x01214020 }, /* HP */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, + [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_mic_no_presence_and_resume, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, - [ALC662_FIXUP_ASUS_MODE8] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x14, 0x99130110 }, /* speaker */ - { 0x12, 0x99a30970 }, /* int-mic */ - { 0x15, 0x01214020 }, /* HP */ - { 0x17, 0x99130111 }, /* speaker */ - { 0x18, 0x01a19840 }, /* mic */ - { 0x21, 0x0121401f }, /* HP */ - { } - }, + [ALC287_FIXUP_LEGION_16ACHG6] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16achg6_speakers, + }, + [ALC287_FIXUP_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + }, + [ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, .chained = true, - .chain_id = ALC662_FIXUP_SKU_IGNORE + .chain_id = ALC285_FIXUP_HP_MUTE_LED, }, - [ALC662_FIXUP_NO_JACK_DETECT] = { + [ALC287_FIXUP_CS35L41_I2C_4] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_no_jack_detect, + .v.func = cs35l41_fixup_i2c_four, }, - [ALC662_FIXUP_ZOTAC_Z68] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x02214020 }, /* Front HP */ - { } - } + [ALC245_FIXUP_CS35L41_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_two, }, - [ALC662_FIXUP_INV_DMIC] = { + [ALC245_FIXUP_CS35L41_SPI_1] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_inv_dmic, + .v.func = cs35l41_fixup_spi_one, }, - [ALC668_FIXUP_DELL_XPS13] = { + [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_dell_xps13, + .v.func = cs35l41_fixup_spi_two, .chained = true, - .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX + .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, - [ALC668_FIXUP_DELL_DISABLE_AAMIX] = { + [ALC245_FIXUP_CS35L41_SPI_4] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, + .v.func = cs35l41_fixup_spi_four, + }, + [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_four, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, + }, + [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 }, + { } + }, .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + .chain_id = ALC285_FIXUP_HP_MUTE_LED, }, - [ALC668_FIXUP_AUTO_MUTE] = { + [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, + .v.func = alc_fixup_dell4_mic_no_presence_quiet, .chained = true, - .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, }, - [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = { + [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */ + { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */ { } }, .chained = true, - .chain_id = ALC662_FIXUP_HEADSET_MODE + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, - [ALC662_FIXUP_HEADSET_MODE] = { + [ALC287_FIXUP_LEGION_16ITHG6] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc662, + .v.func = alc287_fixup_legion_16ithg6_speakers, }, - [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } + [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + // enable left speaker + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x41 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x1a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x42 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x40 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + // enable right speaker + { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2a }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xf }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x46 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x44 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x2 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 }, + + { }, }, + }, + [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MODE + .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK, }, - [ALC668_FIXUP_HEADSET_MODE] = { + [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, + .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2, }, - [ALC662_FIXUP_BASS_MODE4_CHMAP] = { + [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, + .v.func = alc295_fixup_dell_inspiron_top_speakers, .chained = true, - .chain_id = ALC662_FIXUP_ASUS_MODE4 + .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, }, - [ALC662_FIXUP_BASS_16] = { + [ALC236_FIXUP_DELL_DUAL_CODECS] = { .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x16, 0x80106111}, /* bass speaker */ - {} - }, + .v.func = alc1220_fixup_gb_dual_codecs, .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, }, - [ALC662_FIXUP_BASS_1A] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - {0x1a, 0x80106111}, /* bass speaker */ - {} - }, + [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, .chained = true, - .chain_id = ALC662_FIXUP_BASS_CHMAP, + .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, }, - [ALC662_FIXUP_BASS_CHMAP] = { + [ALC287_FIXUP_TAS2781_I2C] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_bass_chmap, + .v.func = tas2781_fixup_tias_i2c, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, - [ALC662_FIXUP_ASUS_Nx50] = { + [ALC245_FIXUP_TAS2781_SPI_2] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_auto_mute_via_amp, + .v.func = tas2781_fixup_spi, .chained = true, - .chain_id = ALC662_FIXUP_BASS_1A + .chain_id = ALC285_FIXUP_HP_GPIO_LED, }, - [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = { + [ALC287_FIXUP_TXNW2781_I2C] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode_alc668, - .chain_id = ALC662_FIXUP_BASS_CHMAP + .v.func = tas2781_fixup_txnw_i2c, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, - [ALC668_FIXUP_ASUS_Nx51] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1a, 0x90170151 }, /* bass speaker */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - {} - }, + [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { + .type = HDA_FIXUP_FUNC, + .v.func = yoga7_14arb7_fixup_i2c, .chained = true, - .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, + .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, - [ALC668_FIXUP_MIC_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, - {} - }, + [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_coefbit, }, - [ALC668_FIXUP_ASUS_G751] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x16, 0x0421101f }, /* HP */ - {} - }, + [ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_v1_coefbit, + }, + [ALC245_FIXUP_HP_X360_MUTE_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_coefbit, .chained = true, - .chain_id = ALC668_FIXUP_MIC_COEF + .chain_id = ALC245_FIXUP_HP_GPIO_LED }, - [ALC891_FIXUP_HEADSET_MODE] = { + [ALC287_FIXUP_THINKPAD_I2S_SPK] = { .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_headset_mode, + .v.func = alc287_fixup_bind_dacs, + .chained = true, + .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, }, - [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ - { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ - { } - }, + [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_bind_dacs, .chained = true, - .chain_id = ALC891_FIXUP_HEADSET_MODE + .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, }, - [ALC662_FIXUP_ACER_VERITON] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x50170120 }, /* no internal speaker */ - { } - } + [ALC2XX_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc2xx_fixup_headset_mic, + }, + [ALC289_FIXUP_DELL_CS35L41_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_two, + .chained = true, + .chain_id = ALC289_FIXUP_DUAL_SPK }, - [ALC892_FIXUP_ASROCK_MOBO] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x40f000f0 }, /* disabled */ - { 0x16, 0x40f000f0 }, /* disabled */ - { } - } + [ALC294_FIXUP_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, }, - [ALC662_FIXUP_USI_FUNC] = { + [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_usi_headset_mic, + .v.func = alc256_fixup_acer_sfg16_micmute_led, }, - [ALC662_FIXUP_USI_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ - { 0x18, 0x01a1903d }, - { } - }, - .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC + [ALC256_FIXUP_HEADPHONE_AMP_VOL] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_decrease_headphone_amp_val, }, - [ALC662_FIXUP_LENOVO_MULTI_CODECS] = { + [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = { .type = HDA_FIXUP_FUNC, - .v.func = alc233_alc662_fixup_lenovo_dual_codecs, + .v.func = alc245_fixup_hp_spectre_x360_eu0xxx, }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { + [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = { .type = HDA_FIXUP_FUNC, - .v.func = alc662_fixup_aspire_ethos_hp, + .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx, }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x15, 0x92130110 }, /* front speakers */ - { 0x18, 0x99130111 }, /* center/subwoofer */ - { 0x1b, 0x11130012 }, /* surround plus jack for HP */ - { } - }, - .chained = true, - .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET + [ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_zbook_firefly_g12a, }, - [ALC671_FIXUP_HP_HEADSET_MIC2] = { + [ALC285_FIXUP_ASUS_GA403U] = { .type = HDA_FIXUP_FUNC, - .v.func = alc671_fixup_hp_headset_mic2, + .v.func = alc285_fixup_asus_ga403u, }, - [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = { + [ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */ + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, { } }, .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC + .chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1 }, - [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { 0x1b, 0x0221144f }, - { } - }, + [ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, .chained = true, - .chain_id = ALC662_FIXUP_USI_FUNC + .chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC, }, - [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = { + [ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x1b, 0x04a1112c }, + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, { } }, + }, + [ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, .chained = true, - .chain_id = ALC668_FIXUP_HEADSET_MIC + .chain_id = ALC285_FIXUP_ASUS_GA403U, }, - [ALC668_FIXUP_HEADSET_MIC] = { + [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_headset_mic, + .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318, .chained = true, - .chain_id = ALC668_FIXUP_MIC_DET_COEF + .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, - [ALC668_FIXUP_MIC_DET_COEF] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 }, - { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 }, - {} - }, + [ALC256_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc256_fixup_chromebook, + .chained = true, + .chain_id = ALC225_FIXUP_HEADSET_JACK }, - [ALC897_FIXUP_LENOVO_HEADSET_MIC] = { + [ALC245_FIXUP_CLEVO_NOISY_MIC] = { .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mic, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, }, - [ALC897_FIXUP_HEADSET_MIC_PIN] = { + [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x03a11050 }, + { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x20a11040 }, /* dock mic */ { } }, .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, - [ALC897_FIXUP_HP_HSMIC_VERB] = { + [ALC233_FIXUP_MEDION_MTL_SPK] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x90170110 }, { } }, }, - [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { + [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, - .v.func = alc897_fixup_lenovo_headset_mode, + .v.func = alc294_fixup_bass_speaker_15, }, - [ALC897_FIXUP_HEADSET_MIC_PIN2] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ - { } - }, + [ALC283_FIXUP_DELL_HP_RESUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_dell_hp_resume, + }, + [ALC294_FIXUP_ASUS_CS35L41_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_spi_two, .chained = true, - .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE + .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC, }, - [ALC897_FIXUP_UNIS_H3C_X500S] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 }, - {} - }, + [ALC274_FIXUP_HP_AIO_BIND_DACS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_hp_aio_bind_dacs, }, - [ALC897_FIXUP_HEADSET_MIC_PIN3] = { + [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11050 }, /* use as headset mic */ + { 0x19, 0x03a11050 }, + { 0x1b, 0x03a11c30 }, { } }, + .chained = true, + .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 + }, + [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, + }, + [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, }, }; -static const struct hda_quirk alc662_fixup_tbl[] = { - SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), - SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS), - SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE), - SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE), - SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), - SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), - SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), - SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), - SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), - SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), - SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), - SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), - SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), - SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), - SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), - SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), - SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), - SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN), - SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), - SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), - SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), - SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), - SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), - SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), - SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), +static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), + SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), + SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), + SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), + SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF), + SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1025, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), + SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER), + SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), + SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), + SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x129c, "Acer SWIFT SF314-55", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x129d, "Acer SWIFT SF313-51", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1300, "Acer SWIFT SF314-56", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC), + SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), + SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), + SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME), + SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), + SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), + SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), + SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13), + SND_PCI_QUIRK(0x1028, 0x0669, "Dell Optiplex 9020m", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x069a, "Dell Vostro 5480", ALC290_FIXUP_SUBWOOFER_HSJACK), + SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), + SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), + SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), + SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), + SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), + SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), + SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), + SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), + SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3), + SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB), + SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET), + SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0a9d, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), + SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), + SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS), + SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS), + SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cc0, "Dell Oasis 13", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK), + SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY), + SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS), + SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS), + SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M), + SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2b5e, "HP 288 Pro G2 MT", ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x802e, "HP Z240 SFF", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC), + SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED), + SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN), + SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), + SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + 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, 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), + SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), + SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), + SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1), + SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), + SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8760, "HP EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED), + SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation", + ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation", + ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f2, "HP ProBook 640 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), + SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP), + SND_PCI_QUIRK(0x103c, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), + SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), + SND_PCI_QUIRK(0x103c, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), + SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c0, "HP ZBook Power 15.6 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + 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, 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), + SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b8a, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b8b, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + 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, 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), + SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + 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, 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), + SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), + SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), + SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d07, "HP Victus 15-fb2xxx (MB 8D07)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), + SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS), + SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ded, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfb, "HP EliteBook 6 G1a 14", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A), + SND_PCI_QUIRK(0x103c, 0x8e1d, "HP ZBook X Gli 16 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2), + 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(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), + SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), + SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), + SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), + SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM), + SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + 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, 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), + SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1493, "ASUS GV601VV/VU/VJ/VQ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G614JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS G513PI/PU/PV", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x14f2, "ASUS VivoBook X515JA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1503, "ASUS G733PY/PZ/PZV/PYV", ALC287_FIXUP_CS35L41_I2C_2), + 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, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS UX7602VI/BZ", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS), + SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY), + SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), + SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), + SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), + SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x1043, 0x1c80, "ASUS VivoBook TP401", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS), + SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1ccf, "ASUS G814JU/JV/JI", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1cdf, "ASUS G814JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1cef, "ASUS G834JY/JZ/JI/JG", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS G713PI/PU/PV/PVN", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), + SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), + SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), + SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), + SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS), + SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), + SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), + SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), + SND_PCI_QUIRK(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1), + SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), + SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), + SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), + SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), + SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT), + SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN), + SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC), + SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), + SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), + SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), + SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), + SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), + 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(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), + SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), + SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP), + SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), + SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), + SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x35a1, "Clevo V3[56]0EN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x35b1, "Clevo V3[57]0WN[MNP]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4041, "Clevo NV4[15]PZ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5015, "Clevo NH5[58]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5017, "Clevo NH7[79]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50e1, "Clevo NH5[58]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50e2, "Clevo NH7[79]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f2, "Clevo NH50E[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f5, "Clevo NH55EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f6, "Clevo NH55DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70f3, "Clevo NH77DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70f4, "Clevo NH77EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70f6, "Clevo NH77DPQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x7724, "Clevo L140AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8550, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8551, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8560, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), + SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x867c, "Clevo NP7[01]PNP", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), + SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x9600, "Clevo N960K[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + 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, 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), + SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC), + SND_PCI_QUIRK(0x1558, 0xa743, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), + SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC), + SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xc018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), + SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST), + SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440), + SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x222d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), + SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318), + SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), + SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7), + SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS), + HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF), + SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), + SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), + SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), + SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ + SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), + 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), + 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), + SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_CS35L41_I2C_4), + SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), + SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), + SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), + SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), + SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), + SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), + SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x505d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), + SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK), + SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), + SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), + SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL), + SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), + SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), + SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), + SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), + SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), + SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ + SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802), + SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X), + SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS), + SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), + SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), + SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), + SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), + SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), + SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), + SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), + SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), + SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), + SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), + SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), + +#if 0 + /* Below is a quirk table taken from the old code. + * Basically the device should work as is without the fixup table. + * If BIOS doesn't give a proper info, enable the corresponding + * fixup entry. + */ + SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", + ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC), + SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC), + SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC), +#endif + {} +}; + +static const struct hda_quirk alc269_fixup_vendor_tbl[] = { + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), + SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), + SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), + {} +}; + +static const struct hda_model_fixup alc269_fixup_models[] = { + {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, + {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, + {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, + {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"}, + {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, + {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, + {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, + {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, + {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"}, + {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, + {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, + {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, + {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, + {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, + {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, + {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"}, + {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, + {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, + {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, + {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, + {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, + {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"}, + {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, + {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, + {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, + {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"}, + {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"}, + {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"}, + {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"}, + {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"}, + {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"}, + {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"}, + {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"}, + {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"}, + {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"}, + {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"}, + {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"}, + {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"}, + {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"}, + {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"}, + {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"}, + {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"}, + {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"}, + {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"}, + {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"}, + {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"}, + {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"}, + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"}, + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"}, + {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"}, + {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"}, + {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"}, + {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, + {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, + {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, + {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"}, + {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, + {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, + {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, + {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, + {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, + {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, + {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, + {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"}, + {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, + {.id = ALC269VB_FIXUP_ASPIRE_E1_COEF, .name = "aspire-e1-coef"}, + {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, + {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, + {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"}, + {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"}, + {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"}, + {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"}, + {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"}, + {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"}, + {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"}, + {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"}, + {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"}, + {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"}, + {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"}, + {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"}, + {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, + {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, + {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, + {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"}, + {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"}, + {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"}, + {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"}, + {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"}, + {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"}, + {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"}, + {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"}, + {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"}, + {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"}, + {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"}, + {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"}, + {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"}, + {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"}, + {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"}, + {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"}, + {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"}, + {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, + {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, + {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, + {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"}, + {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"}, + {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"}, + {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"}, + {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"}, + {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, + {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"}, + {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"}, + {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"}, + {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"}, + {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, + {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, + {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"}, + {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"}, + {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"}, + {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"}, + {.id = ALC285_FIXUP_HP_SPECTRE_X360_DF1, .name = "alc285-hp-spectre-x360-df1"}, + {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"}, + {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"}, + {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"}, + {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"}, + {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"}, + {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"}, + {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"}, + {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, + {} +}; +#define ALC225_STANDARD_PINS \ + {0x21, 0x04211020} + +#define ALC256_STANDARD_PINS \ + {0x12, 0x90a60140}, \ + {0x14, 0x90170110}, \ + {0x21, 0x02211020} + +#define ALC282_STANDARD_PINS \ + {0x14, 0x90170110} + +#define ALC290_STANDARD_PINS \ + {0x12, 0x99a30130} + +#define ALC292_STANDARD_PINS \ + {0x14, 0x90170110}, \ + {0x15, 0x0221401f} + +#define ALC295_STANDARD_PINS \ + {0x12, 0xb7a60130}, \ + {0x14, 0x90170110}, \ + {0x21, 0x04211020} + +#define ALC298_STANDARD_PINS \ + {0x12, 0x90a60130}, \ + {0x21, 0x03211020} + +static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0221, 0x103c, "HP Workstation", ALC221_FIXUP_HP_HEADSET_MIC, + {0x14, 0x01014020}, + {0x17, 0x90170110}, + {0x18, 0x02a11030}, + {0x19, 0x0181303F}, + {0x21, 0x0221102f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE, + {0x12, 0x90a601c0}, + {0x14, 0x90171120}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x90a70130}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x1a, 0x90a70130}, + {0x1b, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60130}, + {0x14, 0x901701a0}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60130}, + {0x14, 0x901701b0}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60150}, + {0x14, 0x901701a0}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60150}, + {0x14, 0x901701b0}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60130}, + {0x1b, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0233, 0x8086, "Intel NUC Skull Canyon", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x1b, 0x01111010}, + {0x1e, 0x01451130}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + {0x12, 0x90a60140}, + {0x14, 0x90170110}, + {0x19, 0x02a11030}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, + {0x14, 0x90170110}, + {0x19, 0x02a11030}, + {0x1a, 0x02a11040}, + {0x1b, 0x01014020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, + {0x14, 0x90170110}, + {0x19, 0x02a11030}, + {0x1a, 0x02a11040}, + {0x1b, 0x01011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x1a, 0x02a11030}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC, + {0x21, 0x02211010}), + SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, + {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60140}, + {0x14, 0x90170110}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60160}, + {0x14, 0x90170120}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x02011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x01011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, + {0x1b, 0x01014020}, + {0x21, 0x0221103f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, + {0x1b, 0x01011020}, + {0x21, 0x0221103f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, + {0x1b, 0x02011020}, + {0x21, 0x0221103f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170150}, + {0x1b, 0x02011020}, + {0x21, 0x0221105f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x1b, 0x01014020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60160}, + {0x14, 0x90170120}, + {0x17, 0x90170140}, + {0x21, 0x0321102f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60160}, + {0x14, 0x90170130}, + {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60160}, + {0x14, 0x90170140}, + {0x21, 0x02211050}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60170}, + {0x14, 0x90170120}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60170}, + {0x14, 0x90170130}, + {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60170}, + {0x14, 0x90171130}, + {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60170}, + {0x14, 0x90170140}, + {0x21, 0x02211050}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60180}, + {0x14, 0x90170130}, + {0x21, 0x02211040}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60180}, + {0x14, 0x90170120}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x1b, 0x01011020}, + {0x21, 0x02211010}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, + {0x14, 0x90170110}, + {0x1b, 0x90a70130}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC, + {0x14, 0x90170110}, + {0x1b, 0x90a70130}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x1a, 0x90a70130}, + {0x1b, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC, + {0x17, 0x90170110}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x15, 0x0421101f}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED, + {0x12, 0x90a60140}, + {0x14, 0x90170110}, + {0x15, 0x0421101f}, + {0x18, 0x02811030}, + {0x1a, 0x04a1103f}, + {0x1b, 0x02011020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x19, 0x03a11020}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x19, 0x03a11020}, + {0x21, 0x03211040}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC282_STANDARD_PINS, + {0x12, 0x99a30130}, + {0x19, 0x04a11020}, + {0x21, 0x0421101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED, + ALC282_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x19, 0x04a11030}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, + ALC282_STANDARD_PINS, + {0x12, 0x90a609c0}, + {0x18, 0x03a11830}, + {0x19, 0x04a19831}, + {0x1a, 0x0481303f}, + {0x1b, 0x04211020}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, + ALC282_STANDARD_PINS, + {0x12, 0x90a60940}, + {0x18, 0x03a11830}, + {0x19, 0x04a19831}, + {0x1a, 0x0481303f}, + {0x1b, 0x04211020}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC282_STANDARD_PINS, + {0x12, 0x90a60130}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60160}, + {0x14, 0x90170120}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC282_STANDARD_PINS, + {0x12, 0x90a60130}, + {0x19, 0x03a11020}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x19, 0x04a11040}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, + {0x14, 0x90170110}, + {0x19, 0x04a11040}, + {0x1d, 0x40600001}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, + {0x14, 0x90170110}, + {0x19, 0x04a11040}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK, + {0x14, 0x90170110}, + {0x17, 0x90170111}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, + {0x17, 0x90170110}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, + {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */ + {0x19, 0x04a11040}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60120}, + {0x14, 0x90170110}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x15, 0x04211040}, + {0x18, 0x90170112}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x15, 0x04211040}, + {0x18, 0x90170110}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x15, 0x0421101f}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x15, 0x04211020}, + {0x1a, 0x04a11040}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x04211020}, + {0x1a, 0x04a11040}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x04211020}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, + ALC290_STANDARD_PINS, + {0x14, 0x90170110}, + {0x15, 0x0421101f}, + {0x1a, 0x04a11020}), + SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x16, 0x01014020}, + {0x19, 0x01a19030}), + SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x90a60140}, + {0x16, 0x01014020}, + {0x18, 0x02a19031}, + {0x19, 0x01a1903e}), + SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x90a60140}), + SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x13, 0x90a60140}, + {0x16, 0x21014020}, + {0x19, 0x21a19030}), + SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x13, 0x90a60140}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE, + {0x17, 0x90170110}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC, + {0x14, 0x90170110}, + {0x1b, 0x90a70130}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60120}, + {0x17, 0x90170110}, + {0x21, 0x04211030}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x17, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x17, 0x90170140}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x17, 0x90170150}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, + {0x12, 0xb7a60140}, + {0x13, 0xb7a60150}, + {0x17, 0x90170110}, + {0x1a, 0x03011020}, + {0x21, 0x03211030}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE, + {0x12, 0xb7a60140}, + {0x17, 0x90170110}, + {0x1a, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x12, 0xb7a60130}, + {0x17, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC, + {0x14, 0x01014010}, + {0x17, 0x90170120}, + {0x18, 0x02a11030}, + {0x19, 0x02a1103f}, + {0x21, 0x0221101f}), + {} +}; + +/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match + * more machines, don't need to match all valid pins, just need to match + * all the pins defined in the tbl. Just because of this reason, it is possible + * that a single machine matches multiple tbls, so there is one limitation: + * at most one tbl is allowed to define for the same vendor and same codec + */ +static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC, + {0x19, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + {0x19, 0x40000000}, + {0x1b, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, + {0x19, 0x40000000}, + {0x1b, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x19, 0x40000000}, + {0x1a, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, + {0x19, 0x40000000}, + {0x1a, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST, + {0x19, 0x40000000}, + {0x1a, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC, + {0x19, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC, + {0x19, 0x40000000}), + {} +}; + +static void alc269_fill_coef(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int val; + + if (spec->codec_variant != ALC269_TYPE_ALC269VB) + return; + + if ((alc_get_coef0(codec) & 0x00ff) < 0x015) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8817); + } + + if ((alc_get_coef0(codec) & 0x00ff) == 0x016) { + alc_write_coef_idx(codec, 0xf, 0x960b); + alc_write_coef_idx(codec, 0xe, 0x8814); + } + + if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { + /* Power up output pin */ + alc_update_coef_idx(codec, 0x04, 0, 1<<11); + } + + if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { + val = alc_read_coef_idx(codec, 0xd); + if (val != -1 && (val & 0x0c00) >> 10 != 0x1) { + /* Capless ramp up clock control */ + alc_write_coef_idx(codec, 0xd, val | (1<<10)); + } + val = alc_read_coef_idx(codec, 0x17); + if (val != -1 && (val & 0x01c0) >> 6 != 0x4) { + /* Class D power on reset */ + alc_write_coef_idx(codec, 0x17, val | (1<<7)); + } + } + + /* HP */ + alc_update_coef_idx(codec, 0x4, 0, 1<<11); +} -#if 0 - /* Below is a quirk table taken from the old code. - * Basically the device should work as is without the fixup table. - * If BIOS doesn't give a proper info, enable the corresponding - * fixup entry. - */ - SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), - SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), - SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), - SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), - SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), - SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), - SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), -#endif - {} -}; +static void alc269_free(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; -static const struct hda_model_fixup alc662_fixup_models[] = { - {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, - {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, - {.id = ALC272_FIXUP_MARIO, .name = "mario"}, - {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, - {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, - {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, - {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, - {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, - {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, - {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, - {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, - {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, - {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, - {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, - {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, - {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, - {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, - {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, - {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, - {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, - {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, - {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, - {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, - {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, - {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, - {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, - {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, - {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, - {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, - {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, - {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, - {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, - {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"}, - {} -}; + if (spec) + hda_component_manager_free(&spec->comps, &comp_master_ops); -static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x17, 0x02211010}, - {0x18, 0x01a19030}, - {0x1a, 0x01813040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, - {0x16, 0x01813030}, - {0x17, 0x02211010}, - {0x18, 0x01a19040}, - {0x21, 0x01014020}), - SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, - {0x14, 0x01014010}, - {0x18, 0x01a19020}, - {0x1a, 0x0181302f}, - {0x1b, 0x0221401f}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30140}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x99a30150}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, - {0x14, 0x90170110}, - {0x15, 0x0321101f}, - {0x16, 0x03011020}), - SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, - {0x12, 0x90a60130}, - {0x14, 0x90170110}, - {0x15, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x17, 0x90170150}, - {0x19, 0x02a11060}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014010}, - {0x18, 0x01a19040}, - {0x1b, 0x01813030}, - {0x21, 0x02211020}), - SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, - {0x14, 0x01014020}, - {0x17, 0x90170110}, - {0x18, 0x01a19050}, - {0x1b, 0x01813040}, - {0x21, 0x02211030}), - {} -}; + alc_free(codec); +} /* */ -static int patch_alc662(struct hda_codec *codec) +static int patch_alc269(struct hda_codec *codec) { struct alc_spec *spec; int err; @@ -13600,25 +7864,187 @@ static int patch_alc662(struct hda_codec *codec) return err; spec = codec->spec; + spec->gen.shared_mic_vref_pin = 0x18; + codec->power_save_node = 0; + spec->en_3kpull_low = true; - spec->shutup = alc_eapd_shutup; - - /* handle multiple HPs as is */ - spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - - alc_fix_pll_init(codec, 0x20, 0x04, 15); + codec->patch_ops.suspend = alc269_suspend; + codec->patch_ops.resume = alc269_resume; + codec->patch_ops.free = alc269_free; + spec->shutup = alc_default_shutup; + spec->init_hook = alc_default_init; switch (codec->core.vendor_id) { - case 0x10ec0668: - spec->init_hook = alc668_restore_default_value; + case 0x10ec0269: + spec->codec_variant = ALC269_TYPE_ALC269VA; + switch (alc_get_coef0(codec) & 0x00f0) { + case 0x0010: + if (codec->bus->pci && + codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) + err = alc_codec_rename(codec, "ALC271X"); + spec->codec_variant = ALC269_TYPE_ALC269VB; + break; + case 0x0020: + if (codec->bus->pci && + codec->bus->pci->subsystem_vendor == 0x17aa && + codec->bus->pci->subsystem_device == 0x21f3) + err = alc_codec_rename(codec, "ALC3202"); + spec->codec_variant = ALC269_TYPE_ALC269VC; + break; + case 0x0030: + spec->codec_variant = ALC269_TYPE_ALC269VD; + break; + default: + alc_fix_pll_init(codec, 0x20, 0x04, 15); + } + if (err < 0) + goto error; + spec->shutup = alc269_shutup; + spec->init_hook = alc269_fill_coef; + alc269_fill_coef(codec); + break; + + case 0x10ec0280: + case 0x10ec0290: + spec->codec_variant = ALC269_TYPE_ALC280; + break; + case 0x10ec0282: + spec->codec_variant = ALC269_TYPE_ALC282; + spec->shutup = alc282_shutup; + spec->init_hook = alc282_init; + break; + case 0x10ec0233: + case 0x10ec0283: + spec->codec_variant = ALC269_TYPE_ALC283; + spec->shutup = alc283_shutup; + spec->init_hook = alc283_init; + break; + case 0x10ec0284: + case 0x10ec0292: + spec->codec_variant = ALC269_TYPE_ALC284; + break; + case 0x10ec0293: + spec->codec_variant = ALC269_TYPE_ALC293; + break; + case 0x10ec0286: + case 0x10ec0288: + spec->codec_variant = ALC269_TYPE_ALC286; + break; + case 0x10ec0298: + spec->codec_variant = ALC269_TYPE_ALC298; + break; + case 0x10ec0235: + case 0x10ec0255: + spec->codec_variant = ALC269_TYPE_ALC255; + spec->shutup = alc256_shutup; + spec->init_hook = alc256_init; + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + spec->codec_variant = ALC269_TYPE_ALC256; + spec->shutup = alc256_shutup; + spec->init_hook = alc256_init; + spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ + if (codec->core.vendor_id == 0x10ec0236 && + codec->bus->pci->vendor != PCI_VENDOR_ID_AMD) + spec->en_3kpull_low = false; + break; + case 0x10ec0257: + spec->codec_variant = ALC269_TYPE_ALC257; + spec->shutup = alc256_shutup; + spec->init_hook = alc256_init; + spec->gen.mixer_nid = 0; + spec->en_3kpull_low = false; + break; + case 0x10ec0215: + case 0x10ec0245: + case 0x10ec0285: + case 0x10ec0289: + if (alc_get_coef0(codec) & 0x0010) + spec->codec_variant = ALC269_TYPE_ALC245; + else + spec->codec_variant = ALC269_TYPE_ALC215; + spec->shutup = alc225_shutup; + spec->init_hook = alc225_init; + spec->gen.mixer_nid = 0; + break; + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + spec->codec_variant = ALC269_TYPE_ALC225; + spec->shutup = alc225_shutup; + spec->init_hook = alc225_init; + spec->gen.mixer_nid = 0; /* no loopback on ALC225, ALC295 and ALC299 */ + break; + case 0x10ec0287: + spec->codec_variant = ALC269_TYPE_ALC287; + spec->shutup = alc225_shutup; + spec->init_hook = alc225_init; + spec->gen.mixer_nid = 0; /* no loopback on ALC287 */ + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + spec->codec_variant = ALC269_TYPE_ALC294; + spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */ + alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */ + spec->init_hook = alc294_init; + break; + case 0x10ec0300: + spec->codec_variant = ALC269_TYPE_ALC300; + spec->gen.mixer_nid = 0; /* no loopback on ALC300 */ + break; + case 0x10ec0222: + case 0x10ec0623: + spec->codec_variant = ALC269_TYPE_ALC623; + spec->shutup = alc222_shutup; + spec->init_hook = alc222_init; + break; + case 0x10ec0700: + case 0x10ec0701: + case 0x10ec0703: + case 0x10ec0711: + spec->codec_variant = ALC269_TYPE_ALC700; + spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */ + alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */ + spec->init_hook = alc294_init; break; + + } + + if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { + spec->has_alc5505_dsp = 1; + spec->init_hook = alc5505_dsp_init; } alc_pre_init(codec); - snd_hda_pick_fixup(codec, alc662_fixup_models, - alc662_fixup_tbl, alc662_fixups); - snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true); + snd_hda_pick_fixup(codec, alc269_fixup_models, + alc269_fixup_tbl, alc269_fixups); + /* FIXME: both TX300 and ROG Strix G17 have the same SSID, and + * the quirk breaks the latter (bko#214101). + * Clear the wrong entry. + */ + if (codec->fixup_id == ALC282_FIXUP_ASUS_TX300 && + codec->core.vendor_id == 0x10ec0294) { + codec_dbg(codec, "Clear wrong fixup for ASUS ROG Strix G17\n"); + codec->fixup_id = HDA_FIXUP_ID_NOT_SET; + } + + snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true); + snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false); + snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, + alc269_fixups); + + /* + * Check whether ACPI describes companion amplifiers that require + * component binding + */ + find_cirrus_companion_amps(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -13626,34 +8052,13 @@ static int patch_alc662(struct hda_codec *codec) if (has_cdefine_beep(codec)) spec->gen.beep_nid = 0x01; - if ((alc_get_coef0(codec) & (1 << 14)) && - codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 && - spec->cdefine.platform_type == 1) { - err = alc_codec_rename(codec, "ALC272X"); - if (err < 0) - goto error; - } - /* automatic parse from the BIOS config */ - err = alc662_parse_auto_config(codec); + err = alc269_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->core.vendor_id) { - case 0x10ec0662: - err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - break; - case 0x10ec0272: - case 0x10ec0663: - case 0x10ec0665: - case 0x10ec0668: - err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - break; - case 0x10ec0273: - err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); - break; - } + if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) { + err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); if (err < 0) goto error; } @@ -13668,39 +8073,9 @@ static int patch_alc662(struct hda_codec *codec) } /* - * ALC680 support - */ - -static int alc680_parse_auto_config(struct hda_codec *codec) -{ - return alc_parse_auto_config(codec, NULL, NULL); -} - -/* - */ -static int patch_alc680(struct hda_codec *codec) -{ - int err; - - /* ALC680 has no aa-loopback mixer */ - err = alc_alloc_spec(codec, 0); - if (err < 0) - return err; - - /* automatic parse from the BIOS config */ - err = alc680_parse_auto_config(codec); - if (err < 0) { - alc_free(codec); - return err; - } - - return 0; -} - -/* - * patch entries + * driver entries */ -static const struct hda_device_id snd_hda_id_realtek[] = { +static const struct hda_device_id snd_hda_id_alc269[] = { HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269), @@ -13715,13 +8090,8 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), - HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), - HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), @@ -13743,53 +8113,22 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269), HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269), - HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), - HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269), HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269), HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269), HDA_CODEC_ENTRY(0x10ec0711, "ALC711", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), - HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), HDA_CODEC_ENTRY(0x19e58326, "HW8326", patch_alc269), {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc269); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Realtek HD-audio codec"); +MODULE_DESCRIPTION("Realtek ALC269 and compatible HD-audio codecs"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); -static struct hda_codec_driver realtek_driver = { - .id = snd_hda_id_realtek, +static struct hda_codec_driver alc269_driver = { + .id = snd_hda_id_alc269, }; -module_hda_codec_driver(realtek_driver); +module_hda_codec_driver(alc269_driver); diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c new file mode 100644 index 0000000000000..94e4a313e5e2e --- /dev/null +++ b/sound/hda/codecs/realtek/alc662.c @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC662 and compatible codecs +// + +#include +#include +#include "realtek.h" + +/* + * ALC662 support + * + * ALC662 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ + +/* + * BIOS auto configuration + */ + +static int alc662_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc662_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 }; + static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + const hda_nid_t *ssids; + + if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || + codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || + codec->core.vendor_id == 0x10ec0671) + ssids = alc663_ssids; + else + ssids = alc662_ssids; + return alc_parse_auto_config(codec, alc662_ignore, ssids); +} + +static void alc272_fixup_mario(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, + (0x3b << AC_AMPCAP_OFFSET_SHIFT) | + (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT))) + codec_warn(codec, "failed to override amp caps for NID 0x2\n"); +} + +/* avoid D3 for keeping GPIO up */ +static unsigned int gpio_led_power_filter(struct hda_codec *codec, + hda_nid_t nid, + unsigned int power_state) +{ + struct alc_spec *spec = codec->spec; + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) + return AC_PWRST_D0; + return power_state; +} + +static void alc662_fixup_led_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + alc_fixup_hp_gpio_led(codec, action, 0x01, 0); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 1; + codec->power_filter = gpio_led_power_filter; + } +} + +static void alc662_usi_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + msleep(200); + snd_hda_gen_hp_automute(codec, jack); + + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + msleep(100); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} + +static void alc662_fixup_usi_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc662_usi_automute_hook; + } +} + +static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, + struct hda_jack_callback *cb) +{ + /* surround speakers at 0x1b already get muted automatically when + * headphones are plugged in, but we have to mute/unmute the remaining + * channels manually: + * 0x15 - front left/front right + * 0x18 - front center/ LFE + */ + if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { + snd_hda_set_pin_ctl_cache(codec, 0x15, 0); + snd_hda_set_pin_ctl_cache(codec, 0x18, 0); + } else { + snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); + snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); + } +} + +static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Pin 0x1b: shared headphones jack and surround speakers */ + if (!is_jack_detectable(codec, 0x1b)) + return; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x1b, + alc662_aspire_ethos_mute_speakers); + /* subwoofer needs an extra GPIO setting to become audible */ + alc_setup_gpio(codec, 0x02); + break; + case HDA_FIXUP_ACT_INIT: + /* Make sure to start in a correct state, i.e. if + * headphones have been plugged in before powering up the system + */ + alc662_aspire_ethos_mute_speakers(codec, NULL); + break; + } +} + +static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + static const struct hda_pintbl pincfgs[] = { + { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */ + { 0x1b, 0x0181304f }, + { } + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.mixer_nid = 0; + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + snd_hda_apply_pincfgs(codec, pincfgs); + break; + case HDA_FIXUP_ACT_INIT: + alc_write_coef_idx(codec, 0x19, 0xa054); + break; + } +} + +static void alc897_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + + snd_hda_gen_hp_automute(codec, jack); + vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP; + snd_hda_set_pin_ctl(codec, 0x1b, vref); +} + +static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.hp_automute_hook = alc897_hp_automute_hook; + spec->no_shutup_pins = 1; + } + if (action == HDA_FIXUP_ACT_PROBE) { + snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100); + } +} + +static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc897_hp_automute_hook; + } +} + +static const struct coef_fw alc668_coefs[] = { + WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), + WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), + WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0), + WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f), + WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940), + WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0), + WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418), + WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468), + WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418), + WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00), + WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000), + WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0), + WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480), + WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0), + WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040), + WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697), + WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab), + WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02), + WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6), + {} +}; + +static void alc668_restore_default_value(struct hda_codec *codec) +{ + alc_process_coef_fw(codec, alc668_coefs); +} + +static void alc_fixup_headset_mode_alc662(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */ + + /* Disable boost for mic-in permanently. (This code is only called + from quirks that guarantee that the headphone is at NID 0x1b.) */ + snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000); + snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP); + } else + alc_fixup_headset_mode(codec, fix, action); +} + +static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + alc_write_coef_idx(codec, 0xc4, 0x8000); + alc_update_coef_idx(codec, 0xc2, ~0xfe, 0); + snd_hda_set_pin_ctl_cache(codec, 0x18, 0); + } + alc_fixup_headset_mode(codec, fix, action); +} + +enum { + ALC662_FIXUP_ASPIRE, + ALC662_FIXUP_LED_GPIO1, + ALC662_FIXUP_IDEAPAD, + ALC272_FIXUP_MARIO, + ALC662_FIXUP_CZC_ET26, + ALC662_FIXUP_CZC_P10T, + ALC662_FIXUP_SKU_IGNORE, + ALC662_FIXUP_HP_RP5800, + ALC662_FIXUP_ASUS_MODE1, + ALC662_FIXUP_ASUS_MODE2, + ALC662_FIXUP_ASUS_MODE3, + ALC662_FIXUP_ASUS_MODE4, + ALC662_FIXUP_ASUS_MODE5, + ALC662_FIXUP_ASUS_MODE6, + ALC662_FIXUP_ASUS_MODE7, + ALC662_FIXUP_ASUS_MODE8, + ALC662_FIXUP_NO_JACK_DETECT, + ALC662_FIXUP_ZOTAC_Z68, + ALC662_FIXUP_INV_DMIC, + ALC662_FIXUP_DELL_MIC_NO_PRESENCE, + ALC668_FIXUP_DELL_MIC_NO_PRESENCE, + ALC662_FIXUP_HEADSET_MODE, + ALC668_FIXUP_HEADSET_MODE, + ALC662_FIXUP_BASS_MODE4_CHMAP, + ALC662_FIXUP_BASS_16, + ALC662_FIXUP_BASS_1A, + ALC662_FIXUP_BASS_CHMAP, + ALC668_FIXUP_AUTO_MUTE, + ALC668_FIXUP_DELL_DISABLE_AAMIX, + ALC668_FIXUP_DELL_XPS13, + ALC662_FIXUP_ASUS_Nx50, + ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, + ALC668_FIXUP_ASUS_Nx51, + ALC668_FIXUP_MIC_COEF, + ALC668_FIXUP_ASUS_G751, + ALC891_FIXUP_HEADSET_MODE, + ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + ALC662_FIXUP_ACER_VERITON, + ALC892_FIXUP_ASROCK_MOBO, + ALC662_FIXUP_USI_FUNC, + ALC662_FIXUP_USI_HEADSET_MODE, + ALC662_FIXUP_LENOVO_MULTI_CODECS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, + ALC671_FIXUP_HP_HEADSET_MIC2, + ALC662_FIXUP_ACER_X2660G_HEADSET_MODE, + ALC662_FIXUP_ACER_NITRO_HEADSET_MODE, + ALC668_FIXUP_ASUS_NO_HEADSET_MIC, + ALC668_FIXUP_HEADSET_MIC, + ALC668_FIXUP_MIC_DET_COEF, + ALC897_FIXUP_LENOVO_HEADSET_MIC, + ALC897_FIXUP_HEADSET_MIC_PIN, + ALC897_FIXUP_HP_HSMIC_VERB, + ALC897_FIXUP_LENOVO_HEADSET_MODE, + ALC897_FIXUP_HEADSET_MIC_PIN2, + ALC897_FIXUP_UNIS_H3C_X500S, + ALC897_FIXUP_HEADSET_MIC_PIN3, +}; + +static const struct hda_fixup alc662_fixups[] = { + [ALC662_FIXUP_ASPIRE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x99130112 }, /* subwoofer */ + { } + } + }, + [ALC662_FIXUP_LED_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_led_gpio1, + }, + [ALC662_FIXUP_IDEAPAD] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x99130112 }, /* subwoofer */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_LED_GPIO1, + }, + [ALC272_FIXUP_MARIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc272_fixup_mario, + }, + [ALC662_FIXUP_CZC_ET26] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x403cc000}, + {0x14, 0x90170110}, /* speaker */ + {0x15, 0x411111f0}, + {0x16, 0x411111f0}, + {0x18, 0x01a19030}, /* mic */ + {0x19, 0x90a7013f}, /* int-mic */ + {0x1a, 0x01014020}, + {0x1b, 0x0121401f}, + {0x1c, 0x411111f0}, + {0x1d, 0x411111f0}, + {0x1e, 0x40478e35}, + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_CZC_P10T] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, + [ALC662_FIXUP_SKU_IGNORE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, + }, + [ALC662_FIXUP_HP_RP5800] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0221201f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE1] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19c20 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { 0x21, 0x0121401f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x18, 0x01a19820 }, /* mic */ + { 0x19, 0x99a3092f }, /* int-mic */ + { 0x1b, 0x0121401f }, /* HP out */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE3] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121441f }, /* HP */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x21, 0x01211420 }, /* HP2 */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE4] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x16, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x21, 0x0121441f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE5] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x0121441f }, /* HP */ + { 0x16, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE6] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x15, 0x01211420 }, /* HP2 */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x1b, 0x0121441f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE7] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x17, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x19, 0x99a3094f }, /* int-mic */ + { 0x1b, 0x01214020 }, /* HP */ + { 0x21, 0x0121401f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_ASUS_MODE8] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x99130110 }, /* speaker */ + { 0x12, 0x99a30970 }, /* int-mic */ + { 0x15, 0x01214020 }, /* HP */ + { 0x17, 0x99130111 }, /* speaker */ + { 0x18, 0x01a19840 }, /* mic */ + { 0x21, 0x0121401f }, /* HP */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_SKU_IGNORE + }, + [ALC662_FIXUP_NO_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC662_FIXUP_ZOTAC_Z68] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x02214020 }, /* Front HP */ + { } + } + }, + [ALC662_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC668_FIXUP_DELL_XPS13] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX + }, + [ALC668_FIXUP_DELL_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + }, + [ALC668_FIXUP_AUTO_MUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE + }, + [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_HEADSET_MODE + }, + [ALC662_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc662, + }, + [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC668_FIXUP_HEADSET_MODE + }, + [ALC668_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc668, + }, + [ALC662_FIXUP_BASS_MODE4_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + .chained = true, + .chain_id = ALC662_FIXUP_ASUS_MODE4 + }, + [ALC662_FIXUP_BASS_16] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x16, 0x80106111}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_CHMAP, + }, + [ALC662_FIXUP_BASS_1A] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x1a, 0x80106111}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_CHMAP, + }, + [ALC662_FIXUP_BASS_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + }, + [ALC662_FIXUP_ASUS_Nx50] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC662_FIXUP_BASS_1A + }, + [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_alc668, + .chain_id = ALC662_FIXUP_BASS_CHMAP + }, + [ALC668_FIXUP_ASUS_Nx51] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1a, 0x90170151 }, /* bass speaker */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + {} + }, + .chained = true, + .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, + }, + [ALC668_FIXUP_MIC_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, + {} + }, + }, + [ALC668_FIXUP_ASUS_G751] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x0421101f }, /* HP */ + {} + }, + .chained = true, + .chain_id = ALC668_FIXUP_MIC_COEF + }, + [ALC891_FIXUP_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode, + }, + [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC891_FIXUP_HEADSET_MODE + }, + [ALC662_FIXUP_ACER_VERITON] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x50170120 }, /* no internal speaker */ + { } + } + }, + [ALC892_FIXUP_ASROCK_MOBO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x40f000f0 }, /* disabled */ + { 0x16, 0x40f000f0 }, /* disabled */ + { } + } + }, + [ALC662_FIXUP_USI_FUNC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_usi_headset_mic, + }, + [ALC662_FIXUP_USI_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */ + { 0x18, 0x01a1903d }, + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC662_FIXUP_LENOVO_MULTI_CODECS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_alc662_fixup_lenovo_dual_codecs, + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_aspire_ethos_hp, + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x92130110 }, /* front speakers */ + { 0x18, 0x99130111 }, /* center/subwoofer */ + { 0x1b, 0x11130012 }, /* surround plus jack for HP */ + { } + }, + .chained = true, + .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET + }, + [ALC671_FIXUP_HP_HEADSET_MIC2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc671_fixup_hp_headset_mic2, + }, + [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ + { 0x1b, 0x0221144f }, + { } + }, + .chained = true, + .chain_id = ALC662_FIXUP_USI_FUNC + }, + [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x04a1112c }, + { } + }, + .chained = true, + .chain_id = ALC668_FIXUP_HEADSET_MIC + }, + [ALC668_FIXUP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mic, + .chained = true, + .chain_id = ALC668_FIXUP_MIC_DET_COEF + }, + [ALC668_FIXUP_MIC_DET_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 }, + {} + }, + }, + [ALC897_FIXUP_LENOVO_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc897_fixup_lenovo_headset_mic, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x03a11050 }, + { } + }, + .chained = true, + .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC + }, + [ALC897_FIXUP_HP_HSMIC_VERB] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + }, + [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc897_fixup_lenovo_headset_mode, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE + }, + [ALC897_FIXUP_UNIS_H3C_X500S] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 }, + {} + }, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN3] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11050 }, /* use as headset mic */ + { } + }, + }, +}; + +static const struct hda_quirk alc662_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3), + SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS), + SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE), + SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE), + SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), + SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), + SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), + SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), + SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), + SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), + SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), + SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), + SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51), + SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), + SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), + SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE), + SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS), + SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), + SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), + SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), + SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), + SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), + SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + +#if 0 + /* Below is a quirk table taken from the old code. + * Basically the device should work as is without the fixup table. + * If BIOS doesn't give a proper info, enable the corresponding + * fixup entry. + */ + SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5), + SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6), + SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4), +#endif + {} +}; + +static const struct hda_model_fixup alc662_fixup_models[] = { + {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, + {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, + {.id = ALC272_FIXUP_MARIO, .name = "mario"}, + {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, + {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, + {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, + {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, + {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"}, + {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"}, + {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, + {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, + {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, + {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, + {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, + {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, + {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, + {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, + {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, + {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, + {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, + {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, + {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, + {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, + {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, + {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, + {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, + {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, + {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, + {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, + {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, + {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, + {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"}, + {} +}; + +static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + {0x17, 0x02211010}, + {0x18, 0x01a19030}, + {0x1a, 0x01813040}, + {0x21, 0x01014020}), + SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + {0x16, 0x01813030}, + {0x17, 0x02211010}, + {0x18, 0x01a19040}, + {0x21, 0x01014020}), + SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, + {0x14, 0x01014010}, + {0x18, 0x01a19020}, + {0x1a, 0x0181302f}, + {0x1b, 0x0221401f}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30130}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30140}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x99a30150}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE, + {0x14, 0x90170110}, + {0x15, 0x0321101f}, + {0x16, 0x03011020}), + SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE, + {0x12, 0x90a60130}, + {0x14, 0x90170110}, + {0x15, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014010}, + {0x17, 0x90170150}, + {0x19, 0x02a11060}, + {0x1b, 0x01813030}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014010}, + {0x18, 0x01a19040}, + {0x1b, 0x01813030}, + {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2, + {0x14, 0x01014020}, + {0x17, 0x90170110}, + {0x18, 0x01a19050}, + {0x1b, 0x01813040}, + {0x21, 0x02211030}), + {} +}; + +/* + */ +static int patch_alc662(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + + spec->shutup = alc_eapd_shutup; + + /* handle multiple HPs as is */ + spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; + + alc_fix_pll_init(codec, 0x20, 0x04, 15); + + switch (codec->core.vendor_id) { + case 0x10ec0668: + spec->init_hook = alc668_restore_default_value; + break; + } + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc662_fixup_models, + alc662_fixup_tbl, alc662_fixups); + snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + if ((alc_get_coef0(codec) & (1 << 14)) && + codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) { + err = alc_codec_rename(codec, "ALC272X"); + if (err < 0) + goto error; + } + + /* automatic parse from the BIOS config */ + err = alc662_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + switch (codec->core.vendor_id) { + case 0x10ec0662: + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + break; + case 0x10ec0272: + case 0x10ec0663: + case 0x10ec0665: + case 0x10ec0668: + err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + break; + case 0x10ec0273: + err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + break; + } + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc662[] = { + HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc662); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC662 and compatible HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc662_driver = { + .id = snd_hda_id_alc662, +}; + +module_hda_codec_driver(alc662_driver); diff --git a/sound/hda/codecs/realtek/alc680.c b/sound/hda/codecs/realtek/alc680.c new file mode 100644 index 0000000000000..baf19ee3bc2f4 --- /dev/null +++ b/sound/hda/codecs/realtek/alc680.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC680 codec +// + +#include +#include +#include "realtek.h" + +static int alc680_parse_auto_config(struct hda_codec *codec) +{ + return alc_parse_auto_config(codec, NULL, NULL); +} + +/* + */ +static int patch_alc680(struct hda_codec *codec) +{ + int err; + + /* ALC680 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + /* automatic parse from the BIOS config */ + err = alc680_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } + + return 0; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc680[] = { + HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc680); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC680 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc680_driver = { + .id = snd_hda_id_alc680, +}; + +module_hda_codec_driver(alc680_driver); diff --git a/sound/hda/codecs/realtek/alc861.c b/sound/hda/codecs/realtek/alc861.c new file mode 100644 index 0000000000000..0eb70b44dc057 --- /dev/null +++ b/sound/hda/codecs/realtek/alc861.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC861 codec +// + +#include +#include +#include "realtek.h" + +static int alc861_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc861_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 }; + return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); +} + +/* Pin config fixes */ +enum { + ALC861_FIXUP_FSC_AMILO_PI1505, + ALC861_FIXUP_AMP_VREF_0F, + ALC861_FIXUP_NO_JACK_DETECT, + ALC861_FIXUP_ASUS_A6RP, + ALC660_FIXUP_ASUS_W7J, +}; + +/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ +static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + unsigned int val; + + if (action != HDA_FIXUP_ACT_INIT) + return; + val = snd_hda_codec_get_pin_target(codec, 0x0f); + if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) + val |= AC_PINCTL_IN_EN; + val |= AC_PINCTL_VREF_50; + snd_hda_set_pin_ctl(codec, 0x0f, val); + spec->gen.keep_vref_in_automute = 1; +} + +static const struct hda_fixup alc861_fixups[] = { + [ALC861_FIXUP_FSC_AMILO_PI1505] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } + } + }, + [ALC861_FIXUP_AMP_VREF_0F] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + }, + [ALC861_FIXUP_NO_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC861_FIXUP_ASUS_A6RP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + .chained = true, + .chain_id = ALC861_FIXUP_NO_JACK_DETECT, + }, + [ALC660_FIXUP_ASUS_W7J] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* ASUS W7J needs a magic pin setup on unused NID 0x10 + * for enabling outputs + */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + { } + }, + } +}; + +static const struct hda_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J), + SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), + {} +}; + +/* + */ +static int patch_alc861(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x15); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x23; + + spec->power_hook = alc_power_eapd; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc861_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc861[] = { + HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), + HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC861 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc861_driver = { + .id = snd_hda_id_alc861, +}; + +module_hda_codec_driver(alc861_driver); diff --git a/sound/hda/codecs/realtek/alc861vd.c b/sound/hda/codecs/realtek/alc861vd.c new file mode 100644 index 0000000000000..ebf274ad730b4 --- /dev/null +++ b/sound/hda/codecs/realtek/alc861vd.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC861-VD codec +// Based on ALC882 +// In addition, an independent DAC +// + +#include +#include +#include "realtek.h" + +static int alc861vd_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids); +} + +enum { + ALC660VD_FIX_ASUS_GPIO1, + ALC861VD_FIX_DALLAS, +}; + +/* exclude VREF80 */ +static void alc861vd_fixup_dallas(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + snd_hda_override_pin_caps(codec, 0x18, 0x00000734); + snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); + } +} + +/* reset GPIO1 */ +static void alc660vd_fixup_asus_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gpio_mask |= 0x02; + alc_fixup_gpio(codec, action, 0x01); +} + +static const struct hda_fixup alc861vd_fixups[] = { + [ALC660VD_FIX_ASUS_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc660vd_fixup_asus_gpio1, + }, + [ALC861VD_FIX_DALLAS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc861vd_fixup_dallas, + }, +}; + +static const struct hda_quirk alc861vd_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS), + SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS), + {} +}; + +/* + */ +static int patch_alc861vd(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x23; + + spec->shutup = alc_eapd_shutup; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc861vd_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc861vd[] = { + HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), + HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861vd); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC861-VD HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc861vd_driver = { + .id = snd_hda_id_alc861vd, +}; + +module_hda_codec_driver(alc861vd_driver); diff --git a/sound/hda/codecs/realtek/alc880.c b/sound/hda/codecs/realtek/alc880.c new file mode 100644 index 0000000000000..2e828ab96dc51 --- /dev/null +++ b/sound/hda/codecs/realtek/alc880.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC880 codec +// + +#include +#include +#include "realtek.h" + +static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) +{ + /* For some reason, the res given from ALC880 is broken. + Here we adjust it properly. */ + snd_hda_jack_unsol_event(codec, res >> 2); +} + +static int alc880_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); +} + +/* + * ALC880 fix-ups + */ +enum { + ALC880_FIXUP_GPIO1, + ALC880_FIXUP_GPIO2, + ALC880_FIXUP_MEDION_RIM, + ALC880_FIXUP_LG, + ALC880_FIXUP_LG_LW25, + ALC880_FIXUP_W810, + ALC880_FIXUP_EAPD_COEF, + ALC880_FIXUP_TCL_S700, + ALC880_FIXUP_VOL_KNOB, + ALC880_FIXUP_FUJITSU, + ALC880_FIXUP_F1734, + ALC880_FIXUP_UNIWILL, + ALC880_FIXUP_UNIWILL_DIG, + ALC880_FIXUP_Z71V, + ALC880_FIXUP_ASUS_W5A, + ALC880_FIXUP_3ST_BASE, + ALC880_FIXUP_3ST, + ALC880_FIXUP_3ST_DIG, + ALC880_FIXUP_5ST_BASE, + ALC880_FIXUP_5ST, + ALC880_FIXUP_5ST_DIG, + ALC880_FIXUP_6ST_BASE, + ALC880_FIXUP_6ST, + ALC880_FIXUP_6ST_DIG, + ALC880_FIXUP_6ST_AUTOMUTE, +}; + +/* enable the volume-knob widget support on NID 0x21 */ +static void alc880_fixup_vol_knob(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_jack_detect_enable_callback(codec, 0x21, + alc_update_knob_master); +} + +static const struct hda_fixup alc880_fixups[] = { + [ALC880_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC880_FIXUP_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, + }, + [ALC880_FIXUP_MEDION_RIM] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_LG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x16, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x1a, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_LG_LW25] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x0181344f }, /* line-in */ + { 0x1b, 0x0321403f }, /* headphone */ + { } + } + }, + [ALC880_FIXUP_W810] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_EAPD_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + {} + }, + }, + [ALC880_FIXUP_TCL_S700] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, + [ALC880_FIXUP_VOL_KNOB] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc880_fixup_vol_knob, + }, + [ALC880_FIXUP_FUJITSU] = { + /* override all pins as BIOS on old Amilo is broken */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x01454140 }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_F1734] = { + /* almost compatible with FUJITSU, but no bass and SPDIF */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, + [ALC880_FIXUP_UNIWILL] = { + /* need to fix HP and speaker pins to be parsed correctly */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { } + }, + }, + [ALC880_FIXUP_UNIWILL_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1f, 0x411111f0 }, + { } + } + }, + [ALC880_FIXUP_Z71V] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x99030120 }, /* speaker */ + { 0x15, 0x0121411f }, /* HP */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19950 }, /* mic-in */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + } + }, + [ALC880_FIXUP_ASUS_W5A] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x90a60160 }, /* mic */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0xb743111e }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO1, + }, + [ALC880_FIXUP_3ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* line-out */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_3ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_3ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_5ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01016412 }, /* surr */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_5ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_5ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_6ST_BASE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x01016412 }, /* surr */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01012414 }, /* side */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x02a19c40 }, /* front-mic */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x0121411f }, /* HP */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_6ST] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_AUTOMUTE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x0121401f }, /* HP with jack detect */ + { } + }, + .chained_before = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, +}; + +static const struct hda_quirk alc880_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), + SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE), + SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), + + /* Below is the copied entries from alc880_quirks.c. + * It's not quite sure whether BIOS sets the correct pin-config table + * on these machines, thus they are kept to be compatible with + * the old static quirks. Once when it's confirmed to work without + * these overrides, it'd be better to remove. + */ + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), + /* default Intel */ + SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), + {} +}; + +static const struct hda_model_fixup alc880_fixup_models[] = { + {.id = ALC880_FIXUP_3ST, .name = "3stack"}, + {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, + {.id = ALC880_FIXUP_5ST, .name = "5stack"}, + {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, + {.id = ALC880_FIXUP_6ST, .name = "6stack"}, + {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, + {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"}, + {} +}; + + +/* + * OK, here we have finally the patch for ALC880 + */ +static int patch_alc880(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + spec->gen.need_dac_fix = 1; + spec->gen.beep_nid = 0x01; + + codec->patch_ops.unsol_event = alc880_unsol_event; + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + alc880_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc880[] = { + HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC880 HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc880_driver = { + .id = snd_hda_id_alc880, +}; + +module_hda_codec_driver(alc880_driver); diff --git a/sound/hda/codecs/realtek/alc882.c b/sound/hda/codecs/realtek/alc882.c new file mode 100644 index 0000000000000..6af2f7fcc6bb3 --- /dev/null +++ b/sound/hda/codecs/realtek/alc882.c @@ -0,0 +1,847 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek ALC882/883/885/888/889 codec support +// +// ALC882 is almost identical with ALC880 but has cleaner and more flexible +// configuration. Each pin widget can choose any input DACs and a mixer. +// Each ADC is connected from a mixer of all inputs. This makes possible +// 6-channel independent captures. +// +// In addition, an independent DAC for the multi-playback (not used in this +// driver yet). +// + +#include +#include +#include "realtek.h" + +/* + * Pin config fixes + */ +enum { + ALC882_FIXUP_ABIT_AW9D_MAX, + ALC882_FIXUP_LENOVO_Y530, + ALC882_FIXUP_PB_M5210, + ALC882_FIXUP_ACER_ASPIRE_7736, + ALC882_FIXUP_ASUS_W90V, + ALC889_FIXUP_CD, + ALC889_FIXUP_FRONT_HP_NO_PRESENCE, + ALC889_FIXUP_VAIO_TT, + ALC888_FIXUP_EEE1601, + ALC886_FIXUP_EAPD, + ALC882_FIXUP_EAPD, + ALC883_FIXUP_EAPD, + ALC883_FIXUP_ACER_EAPD, + ALC882_FIXUP_GPIO1, + ALC882_FIXUP_GPIO2, + ALC882_FIXUP_GPIO3, + ALC889_FIXUP_COEF, + ALC882_FIXUP_ASUS_W2JC, + ALC882_FIXUP_ACER_ASPIRE_4930G, + ALC882_FIXUP_ACER_ASPIRE_8930G, + ALC882_FIXUP_ASPIRE_8930G_VERBS, + ALC885_FIXUP_MACPRO_GPIO, + ALC889_FIXUP_DAC_ROUTE, + ALC889_FIXUP_MBP_VREF, + ALC889_FIXUP_IMAC91_VREF, + ALC889_FIXUP_MBA11_VREF, + ALC889_FIXUP_MBA21_VREF, + ALC889_FIXUP_MP11_VREF, + ALC889_FIXUP_MP41_VREF, + ALC882_FIXUP_INV_DMIC, + ALC882_FIXUP_NO_PRIMARY_HP, + ALC887_FIXUP_ASUS_BASS, + ALC887_FIXUP_BASS_CHMAP, + ALC1220_FIXUP_GB_DUAL_CODECS, + ALC1220_FIXUP_GB_X570, + ALC1220_FIXUP_CLEVO_P950, + ALC1220_FIXUP_CLEVO_PB51ED, + ALC1220_FIXUP_CLEVO_PB51ED_PINS, + ALC887_FIXUP_ASUS_AUDIO, + ALC887_FIXUP_ASUS_HMIC, + ALCS1200A_FIXUP_MIC_VREF, + ALC888VD_FIXUP_MIC_100VREF, +}; + +static void alc889_fixup_coef(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_INIT) + return; + alc_update_coef_idx(codec, 7, 0, 0x2030); +} + +/* set up GPIO at initialization */ +static void alc885_fixup_macpro_gpio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + spec->gpio_write_delay = true; + alc_fixup_gpio3(codec, fix, action); +} + +/* Fix the connection of some pins for ALC889: + * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't + * work correctly (bko#42740) + */ +static void alc889_fixup_dac_route(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* fake the connections during parsing the tree */ + static const hda_nid_t conn1[] = { 0x0c, 0x0d }; + static const hda_nid_t conn2[] = { 0x0e, 0x0f }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2); + snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2); + } else if (action == HDA_FIXUP_ACT_PROBE) { + /* restore the connections */ + static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn); + snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn); + } +} + +/* Set VREF on HP pin */ +static void alc889_fixup_mbp_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 }; + struct alc_spec *spec = codec->spec; + int i; + + if (action != HDA_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); + if (get_defcfg_device(val) != AC_JACK_HP_OUT) + continue; + val = snd_hda_codec_get_pin_target(codec, nids[i]); + val |= AC_PINCTL_VREF_80; + snd_hda_set_pin_ctl(codec, nids[i], val); + spec->gen.keep_vref_in_automute = 1; + break; + } +} + +static void alc889_fixup_mac_pins(struct hda_codec *codec, + const hda_nid_t *nids, int num_nids) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < num_nids; i++) { + unsigned int val; + val = snd_hda_codec_get_pin_target(codec, nids[i]); + val |= AC_PINCTL_VREF_50; + snd_hda_set_pin_ctl(codec, nids[i], val); + } + spec->gen.keep_vref_in_automute = 1; +} + +/* Set VREF on speaker pins on imac91 */ +static void alc889_fixup_imac91_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18, 0x1a }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Set VREF on speaker pins on mba11 */ +static void alc889_fixup_mba11_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18 }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Set VREF on speaker pins on mba21 */ +static void alc889_fixup_mba21_vref(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t nids[] = { 0x18, 0x19 }; + + if (action == HDA_FIXUP_ACT_INIT) + alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids)); +} + +/* Don't take HP output as primary + * Strangely, the speaker output doesn't work on Vaio Z and some Vaio + * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05 + */ +static void alc882_fixup_no_primary_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.no_primary_hp = 1; + spec->gen.no_multi_io = 1; + } +} + +static void alc1220_fixup_gb_x570(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + static const struct coef_fw gb_x570_coefs[] = { + WRITE_COEF(0x07, 0x03c0), + WRITE_COEF(0x1a, 0x01c1), + WRITE_COEF(0x1b, 0x0202), + WRITE_COEF(0x43, 0x3005), + {} + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, gb_x570_coefs); + break; + } +} + +static void alc1220_fixup_clevo_p950(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + static const hda_nid_t conn1[] = { 0x0c }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + alc_update_coef_idx(codec, 0x7, 0, 0x3c3); + /* We therefore want to make sure 0x14 (front headphone) and + * 0x1b (speakers) use the stereo DAC 0x02 + */ + snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1); + snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1); +} + +static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc1220_fixup_clevo_p950(codec, fix, action); + alc_fixup_headset_mode_no_hp_mic(codec, fix, action); +} + +static void alc887_asus_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + unsigned int vref; + + snd_hda_gen_hp_automute(codec, jack); + + if (spec->gen.hp_jack_present) + vref = AC_PINCTL_VREF_80; + else + vref = AC_PINCTL_VREF_HIZ; + snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); +} + +static void alc887_fixup_asus_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action != HDA_FIXUP_ACT_PROBE) + return; + snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); + spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; +} + +static const struct hda_fixup alc882_fixups[] = { + [ALC882_FIXUP_ABIT_AW9D_MAX] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ + { } + } + }, + [ALC882_FIXUP_LENOVO_Y530] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ + { } + } + }, + [ALC882_FIXUP_PB_M5210] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, + {} + } + }, + [ALC882_FIXUP_ACER_ASPIRE_7736] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, + }, + [ALC882_FIXUP_ASUS_W90V] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130110 }, /* fix sequence for CLFE */ + { } + } + }, + [ALC889_FIXUP_CD] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1c, 0x993301f0 }, /* CD */ + { } + } + }, + [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */ + { } + }, + .chained = true, + .chain_id = ALC889_FIXUP_CD, + }, + [ALC889_FIXUP_VAIO_TT] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x17, 0x90170111 }, /* hidden surround speaker */ + { } + } + }, + [ALC888_FIXUP_EEE1601] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, + { } + } + }, + [ALC886_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 }, + { } + } + }, + [ALC882_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + { } + } + }, + [ALC883_FIXUP_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + { } + } + }, + [ALC883_FIXUP_ACER_EAPD] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* eanable EAPD on Acer laptops */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + } + }, + [ALC882_FIXUP_GPIO1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + }, + [ALC882_FIXUP_GPIO2] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, + }, + [ALC882_FIXUP_GPIO3] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio3, + }, + [ALC882_FIXUP_ASUS_W2JC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, + .chained = true, + .chain_id = ALC882_FIXUP_EAPD, + }, + [ALC889_FIXUP_COEF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_coef, + }, + [ALC882_FIXUP_ACER_ASPIRE_4930G] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130111 }, /* CLFE speaker */ + { 0x17, 0x99130112 }, /* surround speaker */ + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC882_FIXUP_ACER_ASPIRE_8930G] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x99130111 }, /* CLFE speaker */ + { 0x1b, 0x99130112 }, /* surround speaker */ + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS, + }, + [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { + /* additional init verbs for Acer Aspire 8930G */ + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Enable all DACs */ + /* DAC DISABLE/MUTE 1? */ + /* setting bits 1-5 disables DAC nids 0x02-0x06 + * apparently. Init=0x38 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, + /* DAC DISABLE/MUTE 2? */ + /* some bit here disables the other DACs. + * Init=0x4900 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 }, + /* DMIC fix + * This laptop has a stereo digital microphone. + * The mics are only 1cm apart which makes the stereo + * useless. However, either the mic or the ALC889 + * makes the signal become a difference/sum signal + * instead of standard stereo, which is annoying. + * So instead we flip this bit which makes the + * codec replicate the sum signal to both channels, + * turning it into a normal mono mic. + */ + /* DMIC_CONTROL? Init value = 0x0001 */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC885_FIXUP_MACPRO_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc885_fixup_macpro_gpio, + }, + [ALC889_FIXUP_DAC_ROUTE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_dac_route, + }, + [ALC889_FIXUP_MBP_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_IMAC91_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_imac91_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_MBA11_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba11_vref, + .chained = true, + .chain_id = ALC889_FIXUP_MBP_VREF, + }, + [ALC889_FIXUP_MBA21_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba21_vref, + .chained = true, + .chain_id = ALC889_FIXUP_MBP_VREF, + }, + [ALC889_FIXUP_MP11_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mba11_vref, + .chained = true, + .chain_id = ALC885_FIXUP_MACPRO_GPIO, + }, + [ALC889_FIXUP_MP41_VREF] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC885_FIXUP_MACPRO_GPIO, + }, + [ALC882_FIXUP_INV_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_inv_dmic, + }, + [ALC882_FIXUP_NO_PRIMARY_HP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc882_fixup_no_primary_hp, + }, + [ALC887_FIXUP_ASUS_BASS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x16, 0x99130130}, /* bass speaker */ + {} + }, + .chained = true, + .chain_id = ALC887_FIXUP_BASS_CHMAP, + }, + [ALC887_FIXUP_BASS_CHMAP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_bass_chmap, + }, + [ALC1220_FIXUP_GB_DUAL_CODECS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_dual_codecs, + }, + [ALC1220_FIXUP_GB_X570] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_gb_x570, + }, + [ALC1220_FIXUP_CLEVO_P950] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_clevo_p950, + }, + [ALC1220_FIXUP_CLEVO_PB51ED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_clevo_pb51ed, + }, + [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + {} + }, + .chained = true, + .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, + }, + [ALC887_FIXUP_ASUS_AUDIO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ + { 0x19, 0x22219420 }, + {} + }, + }, + [ALC887_FIXUP_ASUS_HMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc887_fixup_asus_jack, + .chained = true, + .chain_id = ALC887_FIXUP_ASUS_AUDIO, + }, + [ALCS1200A_FIXUP_MIC_VREF] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, PIN_VREF50 }, /* rear mic */ + { 0x19, PIN_VREF50 }, /* front mic */ + {} + } + }, + [ALC888VD_FIXUP_MIC_100VREF] = { + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, PIN_VREF100 }, /* headset mic */ + {} + } + }, +}; + +static const struct hda_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", + ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", + ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), + SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", + ALC882_FIXUP_ACER_ASPIRE_4930G), + SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), + SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), + SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), + SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), + SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), + SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), + SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), + SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), + SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF), + SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), + SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP), + + /* All Apple entries are in codec SSIDs */ + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF), + SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF), + SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF), + SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF), + + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF), + SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), + SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570), + SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), + SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), + SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), + SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x5802, "Clevo X58[05]WN[RST]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED), + SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), + SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF), + {} +}; + +static const struct hda_model_fixup alc882_fixup_models[] = { + {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, + {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, + {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, + {.id = ALC889_FIXUP_CD, .name = "cd"}, + {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, + {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, + {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, + {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, + {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, + {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, + {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, + {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, + {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, + {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, + {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, + {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, + {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, + {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, + {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, + {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, + {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, + {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, + {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, + {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, + {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, + {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"}, + {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, + {} +}; + +static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950, + {0x14, 0x01014010}, + {0x15, 0x01011012}, + {0x16, 0x01016011}, + {0x18, 0x01a19040}, + {0x19, 0x02a19050}, + {0x1a, 0x0181304f}, + {0x1b, 0x0221401f}, + {0x1e, 0x01456130}), + SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950, + {0x14, 0x01015010}, + {0x15, 0x01011012}, + {0x16, 0x01011011}, + {0x18, 0x01a11040}, + {0x19, 0x02a19050}, + {0x1a, 0x0181104f}, + {0x1b, 0x0221401f}, + {0x1e, 0x01451130}), + {} +}; + +/* + * BIOS auto configuration + */ +/* almost identical with ALC880 parser... */ +static int alc882_parse_auto_config(struct hda_codec *codec) +{ + static const hda_nid_t alc882_ignore[] = { 0x1d, 0 }; + static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 }; + return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids); +} + +/* + */ +static int patch_alc882(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err; + + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; + + spec = codec->spec; + + switch (codec->core.vendor_id) { + case 0x10ec0882: + case 0x10ec0885: + case 0x10ec0900: + case 0x10ec0b00: + case 0x10ec1220: + break; + default: + /* ALC883 and variants */ + alc_fix_pll_init(codec, 0x20, 0x0a, 10); + break; + } + + alc_pre_init(codec); + + snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + alc882_fixups); + snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + + /* automatic parse from the BIOS config */ + err = alc882_parse_auto_config(codec); + if (err < 0) + goto error; + + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + alc_free(codec); + return err; +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_alc882[] = { + HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), + HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), + HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), + HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), + HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc882); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek ALC882 and compatible HD-audio codecs"); +MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); + +static struct hda_codec_driver alc882_driver = { + .id = snd_hda_id_alc882, +}; + +module_hda_codec_driver(alc882_driver); diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c new file mode 100644 index 0000000000000..4ab49e76c304a --- /dev/null +++ b/sound/hda/codecs/realtek/realtek.c @@ -0,0 +1,2314 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek HD-audio codec support code +// + +#include +#include +#include "realtek.h" + +/* + * COEF access helper functions + */ + +static void coef_mutex_lock(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_power_up_pm(codec); + mutex_lock(&spec->coef_mutex); +} + +static void coef_mutex_unlock(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + mutex_unlock(&spec->coef_mutex); + snd_hda_power_down_pm(codec); +} + +static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx) +{ + unsigned int val; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); + return val; +} + +int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx) +{ + unsigned int val; + + coef_mutex_lock(codec); + val = __alc_read_coefex_idx(codec, nid, coef_idx); + coef_mutex_unlock(codec); + return val; +} +EXPORT_SYMBOL_NS_GPL(alc_read_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val); +} + +void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val) +{ + coef_mutex_lock(codec); + __alc_write_coefex_idx(codec, nid, coef_idx, coef_val); + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_write_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set) +{ + unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx); + + if (val != -1) + __alc_write_coefex_idx(codec, nid, coef_idx, + (val & ~mask) | bits_set); +} + +void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set) +{ + coef_mutex_lock(codec); + __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set); + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_coefex_idx, "SND_HDA_CODEC_REALTEK"); + +/* a special bypass for COEF 0; read the cached value at the second time */ +unsigned int alc_get_coef0(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->coef0) + spec->coef0 = alc_read_coef_idx(codec, 0); + return spec->coef0; +} +EXPORT_SYMBOL_NS_GPL(alc_get_coef0, "SND_HDA_CODEC_REALTEK"); + +void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw) +{ + coef_mutex_lock(codec); + for (; fw->nid; fw++) { + if (fw->mask == (unsigned short)-1) + __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val); + else + __alc_update_coefex_idx(codec, fw->nid, fw->idx, + fw->mask, fw->val); + } + coef_mutex_unlock(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_process_coef_fw, "SND_HDA_CODEC_REALTEK"); + +/* + * GPIO setup tables, used in initialization + */ + +/* Enable GPIO mask and set output */ +void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) +{ + struct alc_spec *spec = codec->spec; + + spec->gpio_mask |= mask; + spec->gpio_dir |= mask; + spec->gpio_data |= mask; +} +EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_write_gpio_data(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); +} +EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK"); + +void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, + bool on) +{ + struct alc_spec *spec = codec->spec; + unsigned int oldval = spec->gpio_data; + + if (on) + spec->gpio_data |= mask; + else + spec->gpio_data &= ~mask; + if (oldval != spec->gpio_data) + alc_write_gpio_data(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK"); + +void alc_write_gpio(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->gpio_mask) + return; + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, spec->gpio_mask); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); + if (spec->gpio_write_delay) + msleep(1); + alc_write_gpio_data(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + alc_setup_gpio(codec, mask); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x01); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio1, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x02); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio2, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x03); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio3, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x04); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio4, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + snd_hda_gen_add_micmute_led_cdev(codec, NULL); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_micmute_led, "SND_HDA_CODEC_REALTEK"); + +/* + * Fix hardware PLL issue + * On some codecs, the analog PLL gating control must be off while + * the default value is 1. + */ +void alc_fix_pll(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->pll_nid) + alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx, + 1 << spec->pll_coef_bit, 0); +} +EXPORT_SYMBOL_NS_GPL(alc_fix_pll, "SND_HDA_CODEC_REALTEK"); + +void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_bit) +{ + struct alc_spec *spec = codec->spec; + spec->pll_nid = nid; + spec->pll_coef_idx = coef_idx; + spec->pll_coef_bit = coef_bit; + alc_fix_pll(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_fix_pll_init, "SND_HDA_CODEC_REALTEK"); + +/* update the master volume per volume-knob's unsol event */ +void alc_update_knob_master(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + unsigned int val; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value *uctl; + + kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); + if (!kctl) + return; + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return; + val = snd_hda_codec_read(codec, jack->nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + val &= HDA_AMP_VOLMASK; + uctl->value.integer.value[0] = val; + uctl->value.integer.value[1] = val; + kctl->put(kctl, uctl); + kfree(uctl); +} +EXPORT_SYMBOL_NS_GPL(alc_update_knob_master, "SND_HDA_CODEC_REALTEK"); + +/* Change EAPD to verb control */ +void alc_fill_eapd_coef(struct hda_codec *codec) +{ + int coef; + + coef = alc_get_coef0(codec); + + switch (codec->core.vendor_id) { + case 0x10ec0262: + alc_update_coef_idx(codec, 0x7, 0, 1<<5); + break; + case 0x10ec0267: + case 0x10ec0268: + alc_update_coef_idx(codec, 0x7, 0, 1<<13); + break; + case 0x10ec0269: + if ((coef & 0x00f0) == 0x0010) + alc_update_coef_idx(codec, 0xd, 0, 1<<14); + if ((coef & 0x00f0) == 0x0020) + alc_update_coef_idx(codec, 0x4, 1<<15, 0); + if ((coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + break; + case 0x10ec0280: + case 0x10ec0284: + case 0x10ec0290: + case 0x10ec0292: + alc_update_coef_idx(codec, 0x4, 1<<15, 0); + break; + case 0x10ec0225: + case 0x10ec0295: + case 0x10ec0299: + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + fallthrough; + case 0x10ec0215: + case 0x10ec0236: + case 0x10ec0245: + case 0x10ec0256: + case 0x10ec0257: + case 0x10ec0285: + case 0x10ec0289: + alc_update_coef_idx(codec, 0x36, 1<<13, 0); + fallthrough; + case 0x10ec0230: + case 0x10ec0233: + case 0x10ec0235: + case 0x10ec0255: + case 0x19e58326: + case 0x10ec0282: + case 0x10ec0283: + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + case 0x10ec0300: + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + break; + case 0x10ec0275: + alc_update_coef_idx(codec, 0xe, 0, 1<<0); + break; + case 0x10ec0287: + alc_update_coef_idx(codec, 0x10, 1<<9, 0); + alc_write_coef_idx(codec, 0x8, 0x4ab7); + break; + case 0x10ec0293: + alc_update_coef_idx(codec, 0xa, 1<<13, 0); + break; + case 0x10ec0234: + case 0x10ec0274: + alc_write_coef_idx(codec, 0x6e, 0x0c25); + fallthrough; + case 0x10ec0294: + case 0x10ec0700: + case 0x10ec0701: + case 0x10ec0703: + case 0x10ec0711: + alc_update_coef_idx(codec, 0x10, 1<<15, 0); + break; + case 0x10ec0662: + if ((coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ + break; + case 0x10ec0272: + case 0x10ec0273: + case 0x10ec0663: + case 0x10ec0665: + case 0x10ec0670: + case 0x10ec0671: + case 0x10ec0672: + alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */ + break; + case 0x10ec0222: + case 0x10ec0623: + alc_update_coef_idx(codec, 0x19, 1<<13, 0); + break; + case 0x10ec0668: + alc_update_coef_idx(codec, 0x7, 3<<13, 0); + break; + case 0x10ec0867: + alc_update_coef_idx(codec, 0x4, 1<<10, 0); + break; + case 0x10ec0888: + if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030) + alc_update_coef_idx(codec, 0x7, 1<<5, 0); + break; + case 0x10ec0892: + case 0x10ec0897: + alc_update_coef_idx(codec, 0x7, 1<<5, 0); + break; + case 0x10ec0899: + case 0x10ec0900: + case 0x10ec0b00: + case 0x10ec1168: + case 0x10ec1220: + alc_update_coef_idx(codec, 0x7, 1<<1, 0); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fill_eapd_coef, "SND_HDA_CODEC_REALTEK"); + +/* turn on/off EAPD control (only if available) */ +static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return; + if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + on ? 2 : 0); +} + +/* turn on/off EAPD controls of the codec */ +void alc_auto_setup_eapd(struct hda_codec *codec, bool on) +{ + /* We currently only handle front, HP */ + static const hda_nid_t pins[] = { + 0x0f, 0x10, 0x14, 0x15, 0x17, 0 + }; + const hda_nid_t *p; + for (p = pins; *p; p++) + set_eapd(codec, *p, on); +} +EXPORT_SYMBOL_NS_GPL(alc_auto_setup_eapd, "SND_HDA_CODEC_REALTEK"); + +/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ +int alc_find_ext_mic_pin(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t nid; + unsigned int defcfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + nid = cfg->inputs[i].pin; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + continue; + return nid; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_find_ext_mic_pin, "SND_HDA_CODEC_REALTEK"); + +void alc_headset_mic_no_shutup(struct hda_codec *codec) +{ + const struct hda_pincfg *pin; + int mic_pin = alc_find_ext_mic_pin(codec); + int i; + + /* don't shut up pins when unloading the driver; otherwise it breaks + * the default pin setup at the next load of the driver + */ + if (codec->bus->shutdown) + return; + + snd_array_for_each(&codec->init_pins, i, pin) { + /* use read here for syncing after issuing each verb */ + if (pin->nid != mic_pin) + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + + codec->pins_shutup = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_headset_mic_no_shutup, "SND_HDA_CODEC_REALTEK"); + +void alc_shutup_pins(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->no_shutup_pins) + return; + + switch (codec->core.vendor_id) { + case 0x10ec0236: + case 0x10ec0256: + case 0x10ec0257: + case 0x19e58326: + case 0x10ec0283: + case 0x10ec0285: + case 0x10ec0286: + case 0x10ec0287: + case 0x10ec0288: + case 0x10ec0295: + case 0x10ec0298: + alc_headset_mic_no_shutup(codec); + break; + default: + snd_hda_shutup_pins(codec); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_shutup_pins, "SND_HDA_CODEC_REALTEK"); + +/* generic shutup callback; + * just turning off EAPD and a little pause for avoiding pop-noise + */ +void alc_eapd_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + alc_auto_setup_eapd(codec, false); + if (!spec->no_depop_delay) + msleep(200); + alc_shutup_pins(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_eapd_shutup, "SND_HDA_CODEC_REALTEK"); + +/* additional initialization for ALC888 variants */ +static void alc888_coef_init(struct hda_codec *codec) +{ + switch (alc_get_coef0(codec) & 0x00f0) { + /* alc888-VA */ + case 0x00: + /* alc888-VB */ + case 0x10: + alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */ + break; + } +} + +/* generic EAPD initialization */ +void alc_auto_init_amp(struct hda_codec *codec, int type) +{ + alc_auto_setup_eapd(codec, true); + alc_write_gpio(codec); + switch (type) { + case ALC_INIT_DEFAULT: + switch (codec->core.vendor_id) { + case 0x10ec0260: + alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); + break; + case 0x10ec0880: + case 0x10ec0882: + case 0x10ec0883: + case 0x10ec0885: + alc_update_coef_idx(codec, 7, 0, 0x2030); + break; + case 0x10ec0888: + alc888_coef_init(codec); + break; + } + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_auto_init_amp, "SND_HDA_CODEC_REALTEK"); + +/* get a primary headphone pin if available */ +hda_nid_t alc_get_hp_pin(struct alc_spec *spec) +{ + if (spec->gen.autocfg.hp_pins[0]) + return spec->gen.autocfg.hp_pins[0]; + if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT) + return spec->gen.autocfg.line_out_pins[0]; + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_get_hp_pin, "SND_HDA_CODEC_REALTEK"); + +/* + * Realtek SSID verification + */ + +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2) + +void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_sku_ignore, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_no_depop_delay(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PROBE) { + spec->no_depop_delay = 1; + codec->depop_delay = 0; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_no_depop_delay, "SND_HDA_CODEC_REALTEK"); + +int alc_auto_parse_customize_define(struct hda_codec *codec) +{ + unsigned int ass, tmp, i; + unsigned nid = 0; + struct alc_spec *spec = codec->spec; + + spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ + + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; + } + + if (!codec->bus->pci) + return -1; + ass = codec->core.subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + nid = 0x1d; + if (codec->core.vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + + if (!(ass & 1)) { + codec_info(codec, "%s: SKU not ready 0x%08x\n", + codec->core.chip_name, ass); + return -1; + } + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return -1; + + spec->cdefine.port_connectivity = ass >> 30; + spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; + spec->cdefine.check_sum = (ass >> 16) & 0xf; + spec->cdefine.customization = ass >> 8; +do_sku: + spec->cdefine.sku_cfg = ass; + spec->cdefine.external_amp = (ass & 0x38) >> 3; + spec->cdefine.platform_type = (ass & 0x4) >> 2; + spec->cdefine.swap = (ass & 0x2) >> 1; + spec->cdefine.override = ass & 0x1; + + codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n", + nid, spec->cdefine.sku_cfg); + codec_dbg(codec, "SKU: port_connectivity=0x%x\n", + spec->cdefine.port_connectivity); + codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); + codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); + codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization); + codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp); + codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type); + codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap); + codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_auto_parse_customize_define, "SND_HDA_CODEC_REALTEK"); + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} +/* return true if the given NID is found in the list */ +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + return find_idx_in_nid_list(nid, list, nums) >= 0; +} + +/* check subsystem ID and set up device-specific initialization; + * return 1 if initialized, 0 if invalid SSID + */ +/* 32-bit subsystem ID for BIOS loading in HD Audio codec. + * 31 ~ 16 : Manufacture ID + * 15 ~ 8 : SKU ID + * 7 ~ 0 : Assembly ID + * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 + */ +int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) +{ + unsigned int ass, tmp, i; + unsigned nid; + struct alc_spec *spec = codec->spec; + + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return 0; + goto do_sku; + } + + ass = codec->core.subsystem_id & 0xffff; + if (codec->bus->pci && + ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + /* invalid SSID, check the special NID pin defcfg instead */ + /* + * 31~30 : port connectivity + * 29~21 : reserve + * 20 : PCBEEP input + * 19~16 : Check sum (15:1) + * 15~1 : Custom + * 0 : override + */ + nid = 0x1d; + if (codec->core.vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + codec_dbg(codec, + "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n", + ass, nid); + if (!(ass & 1)) + return 0; + if ((ass >> 30) != 1) /* no physical connection */ + return 0; + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return 0; +do_sku: + codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", + ass & 0xffff, codec->core.vendor_id); + /* + * 0 : override + * 1 : Swap Jack + * 2 : 0 --> Desktop, 1 --> Laptop + * 3~5 : External Amplifier control + * 7~6 : Reserved + */ + tmp = (ass & 0x38) >> 3; /* external Amp control */ + if (spec->init_amp == ALC_INIT_UNDEFINED) { + switch (tmp) { + case 1: + alc_setup_gpio(codec, 0x01); + break; + case 3: + alc_setup_gpio(codec, 0x02); + break; + case 7: + alc_setup_gpio(codec, 0x04); + break; + case 5: + default: + spec->init_amp = ALC_INIT_DEFAULT; + break; + } + } + + /* is laptop or Desktop and enable the function "Mute internal speaker + * when the external headphone out jack is plugged" + */ + if (!(ass & 0x8000)) + return 1; + /* + * 10~8 : Jack location + * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered + * 14~13: Resvered + * 15 : 1 --> enable the function "Mute internal speaker + * when the external headphone out jack is plugged" + */ + if (!alc_get_hp_pin(spec)) { + hda_nid_t nid; + tmp = (ass >> 11) & 0x3; /* HP to chassis */ + nid = ports[tmp]; + if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, + spec->gen.autocfg.line_outs)) + return 1; + spec->gen.autocfg.hp_pins[0] = nid; + } + return 1; +} +EXPORT_SYMBOL_NS_GPL(alc_subsystem_id, "SND_HDA_CODEC_REALTEK"); + +/* Check the validity of ALC subsystem-id + * ports contains an array of 4 pin NIDs for port-A, E, D and I */ +void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) +{ + if (!alc_subsystem_id(codec, ports)) { + struct alc_spec *spec = codec->spec; + if (spec->init_amp == ALC_INIT_UNDEFINED) { + codec_dbg(codec, + "realtek: Enable default setup for auto mode as fallback\n"); + spec->init_amp = ALC_INIT_DEFAULT; + } + } +} +EXPORT_SYMBOL_NS_GPL(alc_ssid_check, "SND_HDA_CODEC_REALTEK"); + +/* inverted digital-mic */ +void alc_fixup_inv_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + spec->gen.inv_dmic_split = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_inv_dmic, "SND_HDA_CODEC_REALTEK"); + +int alc_build_controls(struct hda_codec *codec) +{ + int err; + + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_build_controls, "SND_HDA_CODEC_REALTEK"); + +int alc_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + /* hibernation resume needs the full chip initialization */ + if (is_s4_resume(codec)) + alc_pre_init(codec); + + if (spec->init_hook) + spec->init_hook(codec); + + spec->gen.skip_verbs = 1; /* applied in below */ + snd_hda_gen_init(codec); + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp); + snd_hda_apply_verbs(codec); /* apply verbs here after own init */ + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_init, "SND_HDA_CODEC_REALTEK"); + +void alc_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!snd_hda_get_bool_hint(codec, "shutup")) + return; /* disabled explicitly by hints */ + + if (spec && spec->shutup) + spec->shutup(codec); + else + alc_shutup_pins(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_shutup, "SND_HDA_CODEC_REALTEK"); + +void alc_power_eapd(struct hda_codec *codec) +{ + alc_auto_setup_eapd(codec, false); +} +EXPORT_SYMBOL_NS_GPL(alc_power_eapd, "SND_HDA_CODEC_REALTEK"); + +int alc_suspend(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_suspend, "SND_HDA_CODEC_REALTEK"); + +int alc_resume(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->no_depop_delay) + msleep(150); /* to avoid pop noise */ + codec->patch_ops.init(codec); + snd_hda_regmap_sync(codec); + hda_call_check_power_status(codec, 0x01); + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_resume, "SND_HDA_CODEC_REALTEK"); + +/* + */ +const struct hda_codec_ops alc_patch_ops = { + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .free = alc_free, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, +}; +EXPORT_SYMBOL_NS_GPL(alc_patch_ops, "SND_HDA_CODEC_REALTEK"); + +/* + * Rename codecs appropriately from COEF value or subvendor id + */ +struct alc_codec_rename_table { + unsigned int vendor_id; + unsigned short coef_mask; + unsigned short coef_bits; + const char *name; +}; + +struct alc_codec_rename_pci_table { + unsigned int codec_vendor_id; + unsigned short pci_subvendor; + unsigned short pci_subdevice; + const char *name; +}; + +static const struct alc_codec_rename_table rename_tbl[] = { + { 0x10ec0221, 0xf00f, 0x1003, "ALC231" }, + { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, + { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, + { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, + { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, + { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, + { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, + { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, + { 0x10ec0662, 0xffff, 0x4020, "ALC656" }, + { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, + { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, + { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, + { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, + { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, + { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, + { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, + { } /* terminator */ +}; + +static const struct alc_codec_rename_pci_table rename_pci_tbl[] = { + { 0x10ec0280, 0x1028, 0, "ALC3220" }, + { 0x10ec0282, 0x1028, 0, "ALC3221" }, + { 0x10ec0283, 0x1028, 0, "ALC3223" }, + { 0x10ec0288, 0x1028, 0, "ALC3263" }, + { 0x10ec0292, 0x1028, 0, "ALC3226" }, + { 0x10ec0293, 0x1028, 0, "ALC3235" }, + { 0x10ec0255, 0x1028, 0, "ALC3234" }, + { 0x10ec0668, 0x1028, 0, "ALC3661" }, + { 0x10ec0275, 0x1028, 0, "ALC3260" }, + { 0x10ec0899, 0x1028, 0, "ALC3861" }, + { 0x10ec0298, 0x1028, 0, "ALC3266" }, + { 0x10ec0236, 0x1028, 0, "ALC3204" }, + { 0x10ec0256, 0x1028, 0, "ALC3246" }, + { 0x10ec0225, 0x1028, 0, "ALC3253" }, + { 0x10ec0295, 0x1028, 0, "ALC3254" }, + { 0x10ec0299, 0x1028, 0, "ALC3271" }, + { 0x10ec0670, 0x1025, 0, "ALC669X" }, + { 0x10ec0676, 0x1025, 0, "ALC679X" }, + { 0x10ec0282, 0x1043, 0, "ALC3229" }, + { 0x10ec0233, 0x1043, 0, "ALC3236" }, + { 0x10ec0280, 0x103c, 0, "ALC3228" }, + { 0x10ec0282, 0x103c, 0, "ALC3227" }, + { 0x10ec0286, 0x103c, 0, "ALC3242" }, + { 0x10ec0290, 0x103c, 0, "ALC3241" }, + { 0x10ec0668, 0x103c, 0, "ALC3662" }, + { 0x10ec0283, 0x17aa, 0, "ALC3239" }, + { 0x10ec0292, 0x17aa, 0, "ALC3232" }, + { 0x10ec0257, 0x12f0, 0, "ALC3328" }, + { } /* terminator */ +}; + +static int alc_codec_rename_from_preset(struct hda_codec *codec) +{ + const struct alc_codec_rename_table *p; + const struct alc_codec_rename_pci_table *q; + + for (p = rename_tbl; p->vendor_id; p++) { + if (p->vendor_id != codec->core.vendor_id) + continue; + if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) + return alc_codec_rename(codec, p->name); + } + + if (!codec->bus->pci) + return 0; + for (q = rename_pci_tbl; q->codec_vendor_id; q++) { + if (q->codec_vendor_id != codec->core.vendor_id) + continue; + if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) + continue; + if (!q->pci_subdevice || + q->pci_subdevice == codec->bus->pci->subsystem_device) + return alc_codec_rename(codec, q->name); + } + + return 0; +} + +/* + * Digital-beep handlers + */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP + +/* additional beep mixers; private_value will be overwritten */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), +}; + +/* set up and create beep controls */ +int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir) +{ + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + int i; + + for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_set_beep_amp, "SND_HDA_CODEC_REALTEK"); + +static const struct snd_pci_quirk beep_allow_list[] = { + SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1), + SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1), + SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1), + SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1), + SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1), + /* denylist -- no beep available */ + SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0), + SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0), + {} +}; + +int alc_has_cdefine_beep(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + const struct snd_pci_quirk *q; + q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list); + if (q) + return q->value; + return spec->cdefine.enable_pcbeep; +} +EXPORT_SYMBOL_NS_GPL(alc_has_cdefine_beep, "SND_HDA_CODEC_REALTEK"); + +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ + +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int err; + + err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, + spec->parse_flags); + if (err < 0) + return err; + + if (ssid_nids) + alc_ssid_check(codec, ssid_nids); + + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err; + + return 1; +} +EXPORT_SYMBOL_NS_GPL(alc_parse_auto_config, "SND_HDA_CODEC_REALTEK"); + +/* common preparation job for alc_spec */ +int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) +{ + struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); + int err; + + if (!spec) + return -ENOMEM; + codec->spec = spec; + snd_hda_gen_spec_init(&spec->gen); + spec->gen.mixer_nid = mixer_nid; + spec->gen.own_eapd_ctl = 1; + codec->single_adc_amp = 1; + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1; + codec->forced_resume = 1; + codec->patch_ops = alc_patch_ops; + mutex_init(&spec->coef_mutex); + + err = alc_codec_rename_from_preset(codec); + if (err < 0) { + kfree(spec); + return err; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(alc_alloc_spec, "SND_HDA_CODEC_REALTEK"); + +/* For dual-codec configuration, we need to disable some features to avoid + * conflicts of kctls and PCM streams + */ +void alc_fixup_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + /* disable vmaster */ + spec->gen.suppress_vmaster = 1; + /* auto-mute and auto-mic switch don't work with multiple codecs */ + spec->gen.suppress_auto_mute = 1; + spec->gen.suppress_auto_mic = 1; + /* disable aamix as well */ + spec->gen.mixer_nid = 0; + /* add location prefix to avoid conflicts */ + codec->force_pin_prefix = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */ + { } +}; + +/* override the 2.1 chmap */ +void alc_fixup_bass_chmap(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_BUILD) { + struct alc_spec *spec = codec->spec; + spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_bass_chmap, "SND_HDA_CODEC_REALTEK"); + +/* exported as it's used by multiple codecs */ +void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc_fixup_dual_codecs(codec, fix, action); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* override card longname to provide a unique UCM profile */ + strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); + break; + case HDA_FIXUP_ACT_BUILD: + /* rename Capture controls depending on the codec */ + rename_ctl(codec, "Capture Volume", + codec->addr == 0 ? + "Rear-Panel Capture Volume" : + "Front-Panel Capture Volume"); + rename_ctl(codec, "Capture Switch", + codec->addr == 0 ? + "Rear-Panel Capture Switch" : + "Front-Panel Capture Switch"); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc1220_fixup_gb_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + alc_fixup_dual_codecs(codec, fix, action); + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* override card longname to provide a unique UCM profile */ + strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); + break; + case HDA_FIXUP_ACT_BUILD: + /* rename Capture controls depending on the codec */ + rename_ctl(codec, "Capture Volume", + codec->addr == 0 ? + "Rear-Panel Capture Volume" : + "Front-Panel Capture Volume"); + rename_ctl(codec, "Capture Switch", + codec->addr == 0 ? + "Rear-Panel Capture Switch" : + "Front-Panel Capture Switch"); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc233_alc662_fixup_lenovo_dual_codecs, "SND_HDA_CODEC_REALTEK"); + +static void alc_shutup_dell_xps13(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int hp_pin = alc_get_hp_pin(spec); + + /* Prevent pop noises when headphones are plugged in */ + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(20); +} + +void alc_fixup_dell_xps13(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->gen.input_mux; + int i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise + * it causes a click noise at start up + */ + snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); + spec->shutup = alc_shutup_dell_xps13; + break; + case HDA_FIXUP_ACT_PROBE: + /* Make the internal mic the default input source. */ + for (i = 0; i < imux->num_items; i++) { + if (spec->gen.imux_pins[i] == 0x12) { + spec->gen.cur_mux[0] = i; + break; + } + } + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_dell_xps13, "SND_HDA_CODEC_REALTEK"); + +/* + * headset handling + */ + +static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(delay); +} + +static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay) +{ + if (delay <= 0) + delay = 75; + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(delay); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + msleep(delay); +} + +static const struct coef_fw alc225_pre_hsmode[] = { + UPDATE_COEF(0x4a, 1<<8, 0), + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), + UPDATE_COEF(0x63, 3<<14, 3<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x4a, 3<<10, 3<<10), + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + UPDATE_COEF(0x4a, 3<<10, 0), + {} +}; + +static void alc_headset_mode_unplugged(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ + WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ + WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */ + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */ + WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ + WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */ + WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */ + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x1b, 0x0c0b), + WRITE_COEF(0x45, 0xc429), + UPDATE_COEF(0x35, 0x4000, 0), + WRITE_COEF(0x06, 0x2104), + WRITE_COEF(0x1a, 0x0001), + WRITE_COEF(0x26, 0x0004), + WRITE_COEF(0x32, 0x42a3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0298[] = { + UPDATE_COEF(0x19, 0x1300, 0x0300), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x18, 0x7308), + WRITE_COEF(0x6b, 0xc429), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */ + UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */ + UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */ + WRITE_COEF(0x45, 0xc429), /* Set to TRS type */ + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + {} + }; + static const struct coef_fw coef0668[] = { + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x63, 3<<14, 0), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0100, 0), + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0), + UPDATE_COEF(0x6b, 0xf000, 0x5000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x0c00, 0x0c00), + WRITE_COEF(0x45, 0x5289), + UPDATE_COEF(0x4a, 0x0c00, 0), + {} + }; + + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_hp_mute_disable(codec, 75); + alc_process_coef_fw(codec, coef0256); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0298: + alc_process_coef_fw(codec, coef0298); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0668); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_hp_mute_disable(codec, 75); + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_process_coef_fw(codec, coef0225); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to unplugged mode.\n"); +} + + +static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, + hda_nid_t mic_pin) +{ + static const struct coef_fw coef0255[] = { + WRITE_COEFEX(0x57, 0x03, 0x8aa6), + WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ + {} + }; + static const struct coef_fw coef0256[] = { + UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEFEX(0x57, 0x03, 0x09a3), + WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */ + {} + }; + static const struct coef_fw coef0233[] = { + UPDATE_COEF(0x35, 0, 1<<14), + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x1a, 0x0021), + WRITE_COEF(0x26, 0x008c), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0x00c0, 0), + UPDATE_COEF(0x50, 0x2000, 0), + UPDATE_COEF(0x56, 0x0006, 0), + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x66, 0x0008, 0x0008), + UPDATE_COEF(0x67, 0x2000, 0x2000), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x19, 0xa208), + WRITE_COEF(0x2e, 0xacf0), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */ + UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0xb5, 0x1040), + UPDATE_COEF(0xc3, 0, 1<<12), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x63, 3<<14, 0), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000), + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x6b, 0xf000, 0), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0255); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x45, 0xc489); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0256); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0x4689); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0274); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_write_coef_idx(codec, 0x45, 0xc429); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0233); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0288); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0292: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + /* Set to TRS mode */ + alc_write_coef_idx(codec, 0x45, 0xc429); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0293); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); + fallthrough; + case 0x10ec0221: + case 0x10ec0662: + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0668: + alc_write_coef_idx(codec, 0x11, 0x0001); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0688); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0225); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; + } + codec_dbg(codec, "Headset jack set to mic-in mode.\n"); +} + +static void alc_headset_mode_default(struct hda_codec *codec) +{ + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10), + UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10), + UPDATE_COEF(0x49, 3<<8, 0<<8), + UPDATE_COEF(0x4a, 3<<4, 3<<4), + UPDATE_COEF(0x63, 3<<14, 0), + UPDATE_COEF(0x67, 0xf000, 0x3000), + {} + }; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xc089), + WRITE_COEF(0x45, 0xc489), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + WRITE_COEF(0x49, 0x0049), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xc489), + WRITE_COEFEX(0x57, 0x03, 0x0da3), + WRITE_COEF(0x49, 0x0049), + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/ + WRITE_COEF(0x06, 0x6100), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x06, 0x2100), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x76, 0x000e), + WRITE_COEF(0x6c, 0x2400), + WRITE_COEF(0x6b, 0xc429), + WRITE_COEF(0x18, 0x7308), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xC429), /* Set to TRS type */ + UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0041), + WRITE_COEF(0x15, 0x0d40), + WRITE_COEF(0xb7, 0x802b), + {} + }; + static const struct coef_fw coef0274[] = { + WRITE_COEF(0x45, 0x4289), + UPDATE_COEF(0x4a, 0x0010, 0x0010), + UPDATE_COEF(0x6b, 0x0f00, 0), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x1b, 0x0e4b); + alc_write_coef_idx(codec, 0x45, 0xc089); + msleep(50); + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0286: + case 0x10ec0288: + case 0x10ec0298: + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to headphone (default) mode.\n"); +} + +/* Iphone type */ +static void alc_headset_mode_ctia(struct hda_codec *codec) +{ + int val; + + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */ + WRITE_COEF(0x1b, 0x0e6b), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xd429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xd429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static const struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xd429), /* Set to ctia type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0000), + {} + }; + static const struct coef_fw coef0225_1[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), + UPDATE_COEF(0x63, 3<<14, 2<<14), + {} + }; + static const struct coef_fw coef0225_2[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), + UPDATE_COEF(0x63, 3<<14, 1<<14), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xd689); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0298: + val = alc_read_coef_idx(codec, 0x50); + if (val & (1 << 12)) { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + } else { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + } + break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + val = alc_read_coef_idx(codec, 0x45); + if (val & (1 << 9)) + alc_process_coef_fw(codec, coef0225_2); + else + alc_process_coef_fw(codec, coef0225_1); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0867: + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + } + codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); +} + +/* Nokia type */ +static void alc_headset_mode_omtp(struct hda_codec *codec) +{ + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEFEX(0x57, 0x03, 0x8ea6), + {} + }; + static const struct coef_fw coef0256[] = { + WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */ + WRITE_COEF(0x1b, 0x0e6b), + {} + }; + static const struct coef_fw coef0233[] = { + WRITE_COEF(0x45, 0xe429), + WRITE_COEF(0x1b, 0x0c2b), + WRITE_COEF(0x32, 0x4ea3), + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; + static const struct coef_fw coef0292[] = { + WRITE_COEF(0x6b, 0xe429), + WRITE_COEF(0x76, 0x0008), + WRITE_COEF(0x18, 0x7388), + {} + }; + static const struct coef_fw coef0293[] = { + WRITE_COEF(0x45, 0xe429), /* Set to omtp type */ + UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0x15, 0x0d50), + WRITE_COEF(0xc3, 0x0000), + {} + }; + static const struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), + UPDATE_COEF(0x63, 3<<14, 2<<14), + {} + }; + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_process_coef_fw(codec, coef0256); + alc_hp_enable_unmute(codec, 75); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_write_coef_idx(codec, 0x45, 0xe689); + break; + case 0x10ec0233: + case 0x10ec0283: + alc_process_coef_fw(codec, coef0233); + break; + case 0x10ec0298: + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; + case 0x10ec0292: + alc_process_coef_fw(codec, coef0292); + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, coef0225); + alc_hp_enable_unmute(codec, 75); + break; + } + codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); +} + +static void alc_determine_headset_type(struct hda_codec *codec) +{ + int val; + bool is_ctia = false; + struct alc_spec *spec = codec->spec; + static const struct coef_fw coef0255[] = { + WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/ + WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref + conteol) */ + {} + }; + static const struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ + {} + }; + static const struct coef_fw coef0298[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + UPDATE_COEF(0x19, 0x1300, 0x1300), + {} + }; + static const struct coef_fw coef0293[] = { + UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ + WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ + {} + }; + static const struct coef_fw coef0688[] = { + WRITE_COEF(0x11, 0x0001), + WRITE_COEF(0xb7, 0x802b), + WRITE_COEF(0x15, 0x0d60), + WRITE_COEF(0xc3, 0x0c00), + {} + }; + static const struct coef_fw coef0274[] = { + UPDATE_COEF(0x4a, 0x0010, 0), + UPDATE_COEF(0x4a, 0x8000, 0), + WRITE_COEF(0x45, 0xd289), + UPDATE_COEF(0x49, 0x0300, 0x0300), + {} + }; + + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + + switch (codec->core.vendor_id) { + case 0x10ec0255: + alc_process_coef_fw(codec, coef0255); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0230: + case 0x10ec0236: + case 0x10ec0256: + case 0x19e58326: + alc_write_coef_idx(codec, 0x1b, 0x0e4b); + alc_write_coef_idx(codec, 0x06, 0x6104); + alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3); + + alc_process_coef_fw(codec, coef0255); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + if (!is_ctia) { + alc_write_coef_idx(codec, 0x45, 0xe089); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x0070) == 0x0070) + is_ctia = false; + else + is_ctia = true; + } + alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3); + alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0); + break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_process_coef_fw(codec, coef0274); + msleep(850); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + break; + case 0x10ec0233: + case 0x10ec0283: + alc_write_coef_idx(codec, 0x45, 0xd029); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0298: + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + msleep(100); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + msleep(200); + + val = alc_read_coef_idx(codec, 0x50); + if (val & (1 << 12)) { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + } else { + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010); + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + } + alc_process_coef_fw(codec, coef0298); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + msleep(75); + snd_hda_codec_write(codec, 0x21, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0292: + alc_write_coef_idx(codec, 0x6b, 0xd429); + msleep(300); + val = alc_read_coef_idx(codec, 0x6c); + is_ctia = (val & 0x001c) == 0x001c; + break; + case 0x10ec0293: + alc_process_coef_fw(codec, coef0293); + msleep(300); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x0070) == 0x0070; + break; + case 0x10ec0668: + alc_process_coef_fw(codec, coef0688); + msleep(300); + val = alc_read_coef_idx(codec, 0xbe); + is_ctia = (val & 0x1c02) == 0x1c02; + break; + case 0x10ec0215: + case 0x10ec0225: + case 0x10ec0285: + case 0x10ec0295: + case 0x10ec0289: + case 0x10ec0299: + alc_process_coef_fw(codec, alc225_pre_hsmode); + alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000); + val = alc_read_coef_idx(codec, 0x45); + if (val & (1 << 9)) { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8); + msleep(800); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + } else { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); + msleep(800); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + } + if (!is_ctia) { + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10); + alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8); + msleep(100); + val = alc_read_coef_idx(codec, 0x46); + if ((val & 0x00f0) == 0x00f0) + is_ctia = false; + else + is_ctia = true; + } + alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6); + alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4); + alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000); + break; + case 0x10ec0867: + is_ctia = true; + break; + } + + codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", + str_yes_no(is_ctia)); + spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; +} + +static void alc_update_headset_mode(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + + int new_headset_mode; + + if (!snd_hda_jack_detect(codec, hp_pin)) + new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; + else if (mux_pin == spec->headset_mic_pin) + new_headset_mode = ALC_HEADSET_MODE_HEADSET; + else if (mux_pin == spec->headphone_mic_pin) + new_headset_mode = ALC_HEADSET_MODE_MIC; + else + new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; + + if (new_headset_mode == spec->current_headset_mode) { + snd_hda_gen_update_outputs(codec); + return; + } + + switch (new_headset_mode) { + case ALC_HEADSET_MODE_UNPLUGGED: + alc_headset_mode_unplugged(codec); + spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; + spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; + spec->gen.hp_jack_present = false; + break; + case ALC_HEADSET_MODE_HEADSET: + if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) + alc_determine_headset_type(codec); + if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) + alc_headset_mode_ctia(codec); + else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) + alc_headset_mode_omtp(codec); + spec->gen.hp_jack_present = true; + break; + case ALC_HEADSET_MODE_MIC: + alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); + spec->gen.hp_jack_present = false; + break; + case ALC_HEADSET_MODE_HEADPHONE: + alc_headset_mode_default(codec); + spec->gen.hp_jack_present = true; + break; + } + if (new_headset_mode != ALC_HEADSET_MODE_MIC) { + snd_hda_set_pin_ctl_cache(codec, hp_pin, + AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); + if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin) + snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, + PIN_VREFHIZ); + } + spec->current_headset_mode = new_headset_mode; + + snd_hda_gen_update_outputs(codec); +} + +static void alc_update_headset_mode_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + alc_update_headset_mode(codec); +} + +void alc_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + snd_hda_gen_hp_automute(codec, jack); + alc_update_headset_mode(codec); +} +EXPORT_SYMBOL_NS_GPL(alc_update_headset_jack_cb, "SND_HDA_CODEC_REALTEK"); + +static void alc_probe_headset_mode(struct hda_codec *codec) +{ + int i; + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + + /* Find mic pins */ + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) + spec->headset_mic_pin = cfg->inputs[i].pin; + if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) + spec->headphone_mic_pin = cfg->inputs[i].pin; + } + + WARN_ON(spec->gen.cap_sync_hook); + spec->gen.cap_sync_hook = alc_update_headset_mode_hook; + spec->gen.automute_hook = alc_update_headset_mode; + spec->gen.hp_automute_hook = alc_update_headset_jack_cb; +} + +void alc_fixup_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; + break; + case HDA_FIXUP_ACT_PROBE: + alc_probe_headset_mode(codec); + break; + case HDA_FIXUP_ACT_INIT: + if (is_s3_resume(codec) || is_s4_resume(codec)) { + spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN; + spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN; + } + alc_update_headset_mode(codec); + break; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + } + else + alc_fixup_headset_mode(codec, fix, action); +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mic, "SND_HDA_CODEC_REALTEK"); + +/* update LED status via GPIO */ +void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + int polarity, bool enabled) +{ + if (polarity) + enabled = !enabled; + alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ +} +EXPORT_SYMBOL_NS_GPL(alc_update_gpio_led, "SND_HDA_CODEC_REALTEK"); + +/* turn on/off mic-mute LED via GPIO per capture hook */ +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, !brightness); + return 0; +} + +/* turn on/off mute LED via GPIO per vmaster hook */ +static int gpio_mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, + spec->mute_led_polarity, !brightness); + return 0; +} + +/* setup mute and mic-mute GPIO bits, add hooks appropriately */ +void alc_fixup_hp_gpio_led(struct hda_codec *codec, + int action, + unsigned int mute_mask, + unsigned int micmute_mask) +{ + struct alc_spec *spec = codec->spec; + + alc_fixup_gpio(codec, action, mute_mask | micmute_mask); + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + if (mute_mask) { + spec->gpio_mute_led_mask = mute_mask; + snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set); + } + if (micmute_mask) { + spec->gpio_mic_led_mask = micmute_mask; + snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set); + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_hp_gpio_led, "SND_HDA_CODEC_REALTEK"); + +/* suppress the jack-detection */ +void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_no_jack_detect, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_disable_aamix(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + /* Disable AA-loopback as it causes white noise */ + spec->gen.mixer_nid = 0; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_disable_aamix, "SND_HDA_CODEC_REALTEK"); + +void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct alc_spec *spec = codec->spec; + spec->gen.auto_mute_via_amp = 1; + } +} +EXPORT_SYMBOL_NS_GPL(alc_fixup_auto_mute_via_amp, "SND_HDA_CODEC_REALTEK"); + +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek HD-audio codec helper"); diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h new file mode 100644 index 0000000000000..ac142f2540e3f --- /dev/null +++ b/sound/hda/codecs/realtek/realtek.h @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Realtek HD-audio codec support code +// + +#ifndef __HDA_REALTEK_H +#define __HDA_REALTEK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_beep.h" +#include "hda_jack.h" +#include "../generic.h" +#include "../side-codecs/hda_component.h" + +/* extra amp-initialization sequence types */ +enum { + ALC_INIT_UNDEFINED, + ALC_INIT_NONE, + ALC_INIT_DEFAULT, +}; + +enum { + ALC_HEADSET_MODE_UNKNOWN, + ALC_HEADSET_MODE_UNPLUGGED, + ALC_HEADSET_MODE_HEADSET, + ALC_HEADSET_MODE_MIC, + ALC_HEADSET_MODE_HEADPHONE, +}; + +enum { + ALC_HEADSET_TYPE_UNKNOWN, + ALC_HEADSET_TYPE_CTIA, + ALC_HEADSET_TYPE_OMTP, +}; + +enum { + ALC_KEY_MICMUTE_INDEX, +}; + +struct alc_customize_define { + unsigned int sku_cfg; + unsigned char port_connectivity; + unsigned char check_sum; + unsigned char customization; + unsigned char external_amp; + unsigned int enable_pcbeep:1; + unsigned int platform_type:1; + unsigned int swap:1; + unsigned int override:1; + unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ +}; + +struct alc_coef_led { + unsigned int idx; + unsigned int mask; + unsigned int on; + unsigned int off; +}; + +struct alc_spec { + struct hda_gen_spec gen; /* must be at head */ + + /* codec parameterization */ + struct alc_customize_define cdefine; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + + /* GPIO bits */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + bool gpio_write_delay; /* add a delay before writing gpio_data */ + + /* mute LED for HP laptops, see vref_mute_led_set() */ + int mute_led_polarity; + int micmute_led_polarity; + hda_nid_t mute_led_nid; + hda_nid_t cap_mute_led_nid; + + unsigned int gpio_mute_led_mask; + unsigned int gpio_mic_led_mask; + struct alc_coef_led mute_led_coef; + struct alc_coef_led mic_led_coef; + struct mutex coef_mutex; + + hda_nid_t headset_mic_pin; + hda_nid_t headphone_mic_pin; + int current_headset_mode; + int current_headset_type; + + /* hooks */ + void (*init_hook)(struct hda_codec *codec); + void (*power_hook)(struct hda_codec *codec); + void (*shutup)(struct hda_codec *codec); + + int init_amp; + int codec_variant; /* flag for other variants */ + unsigned int has_alc5505_dsp:1; + unsigned int no_depop_delay:1; + unsigned int done_hp_init:1; + unsigned int no_shutup_pins:1; + unsigned int ultra_low_power:1; + unsigned int has_hs_key:1; + unsigned int no_internal_mic_pin:1; + unsigned int en_3kpull_low:1; + int num_speaker_amps; + + /* for PLL fix */ + hda_nid_t pll_nid; + unsigned int pll_coef_idx, pll_coef_bit; + unsigned int coef0; + struct input_dev *kb_dev; + u8 alc_mute_keycode_map[1]; + + /* component binding */ + struct hda_component_parent comps; +}; + +int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx); +void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_val); +void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int mask, + unsigned int bits_set); +#define alc_read_coef_idx(codec, coef_idx) \ + alc_read_coefex_idx(codec, 0x20, coef_idx) +#define alc_write_coef_idx(codec, coef_idx, coef_val) \ + alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val) +#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \ + alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set) + +unsigned int alc_get_coef0(struct hda_codec *codec); + +/* coef writes/updates batch */ +struct coef_fw { + unsigned char nid; + unsigned char idx; + unsigned short mask; + unsigned short val; +}; + +#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \ + { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) } +#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val) +#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val) +#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val) + +void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw); + +/* + * GPIO helpers + */ +void alc_setup_gpio(struct hda_codec *codec, unsigned int mask); +void alc_write_gpio_data(struct hda_codec *codec); +void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, + bool on); +void alc_write_gpio(struct hda_codec *codec); + +/* common GPIO fixups */ +void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask); +void alc_fixup_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio2(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio3(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +/* + * Common init code, callbacks and helpers + */ +void alc_fix_pll(struct hda_codec *codec); +void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, + unsigned int coef_idx, unsigned int coef_bit); +void alc_fill_eapd_coef(struct hda_codec *codec); +void alc_auto_setup_eapd(struct hda_codec *codec, bool on); + +int alc_find_ext_mic_pin(struct hda_codec *codec); +void alc_headset_mic_no_shutup(struct hda_codec *codec); +void alc_shutup_pins(struct hda_codec *codec); +void alc_eapd_shutup(struct hda_codec *codec); +void alc_auto_init_amp(struct hda_codec *codec, int type); +hda_nid_t alc_get_hp_pin(struct alc_spec *spec); +int alc_auto_parse_customize_define(struct hda_codec *codec); +int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports); +void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports); +int alc_build_controls(struct hda_codec *codec); +void alc_update_knob_master(struct hda_codec *codec, + struct hda_jack_callback *jack); + +static inline void alc_pre_init(struct hda_codec *codec) +{ + alc_fill_eapd_coef(codec); +} + +#define is_s3_resume(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME) +#define is_s4_resume(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE) +#define is_s4_suspend(codec) \ + ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE) + +int alc_init(struct hda_codec *codec); +void alc_shutup(struct hda_codec *codec); +void alc_power_eapd(struct hda_codec *codec); +int alc_suspend(struct hda_codec *codec); +int alc_resume(struct hda_codec *codec); + +#define alc_free snd_hda_gen_free + +int alc_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids, + const hda_nid_t *ssid_nids); +int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid); + +extern const struct hda_codec_ops alc_patch_ops; + +#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir); +int alc_has_cdefine_beep(struct hda_codec *codec); +#define set_beep_amp alc_set_beep_amp +#define has_cdefine_beep alc_has_cdefine_beep +#else +#define set_beep_amp(spec, nid, idx, dir) 0 +#define has_cdefine_beep(codec) 0 +#endif + +static inline void rename_ctl(struct hda_codec *codec, const char *oldname, + const char *newname) +{ + struct snd_kcontrol *kctl; + + kctl = snd_hda_find_mixer_ctl(codec, oldname); + if (kctl) + snd_ctl_rename(codec->card, kctl, newname); +} + +/* Common fixups */ +void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_no_depop_delay(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_inv_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_bass_chmap(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_headset_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack); +void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + int polarity, bool enabled); +void alc_fixup_hp_gpio_led(struct hda_codec *codec, + int action, + unsigned int mute_mask, + unsigned int micmute_mask); +void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_disable_aamix(struct hda_codec *codec, + const struct hda_fixup *fix, int action); +void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +/* device-specific, but used by multiple codec drivers */ +void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); +void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); +void alc_fixup_dell_xps13(struct hda_codec *codec, + const struct hda_fixup *fix, int action); + +#endif /* __HDA_REALTEK_H */ -- GitLab From 73cd0490819d2a693928c5977280dd31b756cb42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:11 +0200 Subject: [PATCH 639/868] ALSA: hda/hdmi: Split vendor codec drivers In the past, we unified HD-audio HDMI codec driver once with a slight hope that more vendors will follow the standard, but in reality, the driver received more and more vendor-specific code. In order to make the messy code a bit more understandable, this patch splits the HDMI codec driver into multiple drivers again. Namely, the vendor-specific code for Intel, AMD and Nvidia are moved into the own drivers, while we split the common HDMI code to two drivers, the generic HDMI driver and the simple HDMI driver. So, now we have: - The generic HDMI driver (snd-hda-codec-hdmi): providing the common helpers, also supports Glenfly HDMI codecs and some other codecs that don't need vendor-specific stuff - The simple HDMI driver (snd-hda-codec-simplehdmi): devices with no dynamic PCM assignment and with fixed channels, mostly used by some other drivers, but this driver alone suffices for VIA HDMI codec support, too - Intel HDMI driver (snd-hda-codec-intelhdmi): bound with i915 / Xe DRM, based on the generic HDMI driver - AMD/ATI HDMI driver (snd-hda-codec-atihdmi): optionally bound with radeon / amdgpu DRM, based on the generic HDMI driver - Nvidia HDMI driver (snd-hda-codec-nvhdmi); optionally bound with nouveau DRM, based on the generic HDMI driver - Legacy Nvidia HDMI driver (snd-hda-codec-nvhdmi-mcp): for 2ch or 8ch outputs, based on the simple HDMI driver - Nvidia Tegra HDMI driver (snd-hda-codec-tegrahdmi): based on the generic HDMI driver Along with the driver split, the enable_silent_stream module option is moved to snd-hda-codec-intelhdmi, too, as it's an Intel-specific feature. Most of the changes here are just to split and move the code to different files, as well as to rename/expose the functions that are commonly used by drivers. The silent stream handling code is slightly modified for putting the stuff into Intel driver; now a new callback "silent_stream" is defined in hdmi_ops, and it's called in silent_stream_enable() and *_disable() functions. The runtime-PM handling in silent_stream_enable() was cleaned up, and rather taking the runtime PM refcount in the silent_stream() callback appropriately, instead. Other than that, there should be no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-9-tiwai@suse.de --- sound/hda/codecs/Kconfig | 31 +- sound/hda/codecs/hdmi/Kconfig | 68 + sound/hda/codecs/hdmi/Makefile | 12 + sound/hda/codecs/hdmi/atihdmi.c | 606 +++++++ sound/hda/codecs/hdmi/eld.c | 172 -- sound/hda/codecs/hdmi/hdmi.c | 2465 +--------------------------- sound/hda/codecs/hdmi/hdmi_local.h | 302 ++++ sound/hda/codecs/hdmi/intelhdmi.c | 760 +++++++++ sound/hda/codecs/hdmi/nvhdmi-mcp.c | 382 +++++ sound/hda/codecs/hdmi/nvhdmi.c | 218 +++ sound/hda/codecs/hdmi/simplehdmi.c | 241 +++ sound/hda/codecs/hdmi/tegrahdmi.c | 313 ++++ sound/hda/common/hda_local.h | 4 - 13 files changed, 2993 insertions(+), 2581 deletions(-) create mode 100644 sound/hda/codecs/hdmi/Kconfig create mode 100644 sound/hda/codecs/hdmi/atihdmi.c create mode 100644 sound/hda/codecs/hdmi/hdmi_local.h create mode 100644 sound/hda/codecs/hdmi/intelhdmi.c create mode 100644 sound/hda/codecs/hdmi/nvhdmi-mcp.c create mode 100644 sound/hda/codecs/hdmi/nvhdmi.c create mode 100644 sound/hda/codecs/hdmi/simplehdmi.c create mode 100644 sound/hda/codecs/hdmi/tegrahdmi.c diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig index 0bb675ab84fe5..e8c2efd2efb6e 100644 --- a/sound/hda/codecs/Kconfig +++ b/sound/hda/codecs/Kconfig @@ -35,21 +35,6 @@ config SND_HDA_CODEC_VIA comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_VIA=m -config SND_HDA_CODEC_HDMI - tristate "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS - select SND_PCM_ELD - help - Say Y or M here to include HDMI and DisplayPort HD-audio codec - support in snd-hda-intel driver. This includes all AMD/ATI, - Intel and Nvidia HDMI/DisplayPort codecs. - - Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS - to assure the multiple streams for DP-MST support. - -comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m - config SND_HDA_CODEC_CONEXANT tristate "Build Conexant HD-audio codec support" select SND_HDA_GENERIC @@ -134,23 +119,9 @@ config SND_HDA_GENERIC comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_GENERIC=m -config SND_HDA_INTEL_HDMI_SILENT_STREAM - bool "Enable Silent Stream always for HDMI" - depends on SND_HDA_INTEL - help - Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream - for HDMI on hardware that supports the feature. - - When enabled, the HDMI/DisplayPort codec will continue to provide - a continuous clock and a valid but silent data stream to - any connected external receiver. This allows to avoid gaps - at start of playback. Many receivers require multiple seconds - to start playing audio after the clock has been stopped. - This feature can impact power consumption as resources - are kept reserved both at transmitter and receiver. - source "sound/hda/codecs/realtek/Kconfig" source "sound/hda/codecs/cirrus/Kconfig" +source "sound/hda/codecs/hdmi/Kconfig" source "sound/hda/codecs/side-codecs/Kconfig" endif # SND_HDA diff --git a/sound/hda/codecs/hdmi/Kconfig b/sound/hda/codecs/hdmi/Kconfig new file mode 100644 index 0000000000000..498000d2c6ae0 --- /dev/null +++ b/sound/hda/codecs/hdmi/Kconfig @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SND_HDA_CODEC_HDMI + tristate "Generic HDMI/DisplayPort HD-audio codec support" + select SND_DYNAMIC_MINORS + select SND_PCM_ELD + help + Say Y or M here to include Generic HDMI and DisplayPort HD-audio + codec support. + + Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS + to assure the multiple streams for DP-MST support. + +config SND_HDA_CODEC_HDMI_SIMPLE + tristate "Simple HDMI/DisplayPort HD-audio codec support" + help + Say Y or M here to include Simple HDMI and DisplayPort HD-audio + codec support for VIA and other codecs. + +config SND_HDA_CODEC_HDMI_INTEL + tristate "Intel HDMI/DisplayPort HD-audio codec support" + select SND_HDA_CODEC_HDMI + help + Say Y or M here to include Intel graphics HDMI and DisplayPort + HD-audio codec support. + +config SND_HDA_INTEL_HDMI_SILENT_STREAM + bool "Enable Silent Stream always for HDMI" + depends on SND_HDA_CODEC_HDMI_INTEL + help + Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream + for HDMI on hardware that supports the feature. + + When enabled, the HDMI/DisplayPort codec will continue to provide + a continuous clock and a valid but silent data stream to + any connected external receiver. This allows to avoid gaps + at start of playback. Many receivers require multiple seconds + to start playing audio after the clock has been stopped. + This feature can impact power consumption as resources + are kept reserved both at transmitter and receiver. + +config SND_HDA_CODEC_HDMI_ATI + tristate "AMD/ATI HDMI/DisplayPort HD-audio codec support" + select SND_HDA_CODEC_HDMI + help + Say Y or M here to include AMD/ATI graphics HDMI and DisplayPort + HD-audio codec support. + +config SND_HDA_CODEC_HDMI_NVIDIA + tristate "Nvidia HDMI/DisplayPort HD-audio codec support" + select SND_HDA_CODEC_HDMI + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for the recent Nvidia graphics cards. + +config SND_HDA_CODEC_HDMI_NVIDIA_MCP + tristate "Legacy Nvidia HDMI/DisplayPort HD-audio codec support" + select SND_HDA_CODEC_HDMI_SIMPLE + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for the legacy Nvidia graphics like MCP73, MCP67, MCP77/78. + +config SND_HDA_CODEC_HDMI_TEGRA + tristate "Nvidia Tegra HDMI/DisplayPort HD-audio codec support" + select SND_HDA_CODEC_HDMI + help + Say Y or M here to include HDMI and DisplayPort HD-audio codec + support for Nvidia Tegra. diff --git a/sound/hda/codecs/hdmi/Makefile b/sound/hda/codecs/hdmi/Makefile index 371818d4e9b20..c07a0a71b64fa 100644 --- a/sound/hda/codecs/hdmi/Makefile +++ b/sound/hda/codecs/hdmi/Makefile @@ -2,5 +2,17 @@ subdir-ccflags-y += -I$(src)/../../common snd-hda-codec-hdmi-y := hdmi.o eld.o +snd-hda-codec-simplehdmi-y := simplehdmi.o +snd-hda-codec-intelhdmi-y := intelhdmi.o +snd-hda-codec-atihdmi-y := atihdmi.o +snd-hda-codec-nvhdmi-y := nvhdmi.o +snd-hda-codec-nvhdmi-mcp-y := nvhdmi-mcp.o +snd-hda-codec-tegrahdmi-y := tegrahdmi.o obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_SIMPLE) += snd-hda-codec-simplehdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_INTEL) += snd-hda-codec-intelhdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_ATI) += snd-hda-codec-atihdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA) += snd-hda-codec-nvhdmi.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA_MCP) += snd-hda-codec-nvhdmi-mcp.o +obj-$(CONFIG_SND_HDA_CODEC_HDMI_TEGRA) += snd-hda-codec-tegrahdmi.o diff --git a/sound/hda/codecs/hdmi/atihdmi.c b/sound/hda/codecs/hdmi/atihdmi.c new file mode 100644 index 0000000000000..e23995cc1b8c3 --- /dev/null +++ b/sound/hda/codecs/hdmi/atihdmi.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ATI/AMD codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +#define is_amdhdmi_rev3_or_later(codec) \ + ((codec)->core.vendor_id == 0x1002aa01 && \ + ((codec)->core.revision_id & 0xff00) >= 0x0300) +#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) + +/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ +#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 +#define ATI_VERB_SET_DOWNMIX_INFO 0x772 +#define ATI_VERB_SET_MULTICHANNEL_01 0x777 +#define ATI_VERB_SET_MULTICHANNEL_23 0x778 +#define ATI_VERB_SET_MULTICHANNEL_45 0x779 +#define ATI_VERB_SET_MULTICHANNEL_67 0x77a +#define ATI_VERB_SET_HBR_CONTROL 0x77c +#define ATI_VERB_SET_MULTICHANNEL_1 0x785 +#define ATI_VERB_SET_MULTICHANNEL_3 0x786 +#define ATI_VERB_SET_MULTICHANNEL_5 0x787 +#define ATI_VERB_SET_MULTICHANNEL_7 0x788 +#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 +#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 +#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 +#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 +#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 +#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 +#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a +#define ATI_VERB_GET_HBR_CONTROL 0xf7c +#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 +#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 +#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 +#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 +#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 + +/* AMD specific HDA cvt verbs */ +#define ATI_VERB_SET_RAMP_RATE 0x770 +#define ATI_VERB_GET_RAMP_RATE 0xf70 + +#define ATI_OUT_ENABLE 0x1 + +#define ATI_MULTICHANNEL_MODE_PAIRED 0 +#define ATI_MULTICHANNEL_MODE_SINGLE 1 + +#define ATI_HBR_CAPABLE 0x01 +#define ATI_HBR_ENABLE 0x10 + +/* ATI/AMD specific ELD emulation */ + +#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 +#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 +#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 +#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 +#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b +#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 +#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 + +#define ATI_SPKALLOC_SPKALLOC 0x007f +#define ATI_SPKALLOC_TYPE_HDMI 0x0100 +#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 + +/* first three bytes are just standard SAD */ +#define ATI_AUDIODESC_CHANNELS 0x00000007 +#define ATI_AUDIODESC_RATES 0x0000ff00 +#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 + +/* in standard HDMI VSDB format */ +#define ATI_DELAY_VIDEO_LATENCY 0x000000ff +#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 + +enum ati_sink_info_idx { + ATI_INFO_IDX_MANUFACTURER_ID = 0, + ATI_INFO_IDX_PRODUCT_ID = 1, + ATI_INFO_IDX_SINK_DESC_LEN = 2, + ATI_INFO_IDX_PORT_ID_LOW = 3, + ATI_INFO_IDX_PORT_ID_HIGH = 4, + ATI_INFO_IDX_SINK_DESC_FIRST = 5, + ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ +}; + +static int get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, bool rev3_or_later) +{ + int spkalloc, ati_sad, aud_synch; + int sink_desc_len = 0; + int pos, i; + + /* ATI/AMD does not have ELD, emulate it */ + + spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); + + if (spkalloc <= 0) { + codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n"); + return -EINVAL; + } + + memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); + + /* version */ + buf[0] = ELD_VER_CEA_861D << 3; + + /* speaker allocation from EDID */ + buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; + + /* is DisplayPort? */ + if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) + buf[5] |= 0x04; + + pos = ELD_FIXED_BYTES; + + if (rev3_or_later) { + int sink_info; + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 8); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 12); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 16); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 18); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); + sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + + if (sink_desc_len > ELD_MAX_MNL) { + codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", + sink_desc_len); + sink_desc_len = ELD_MAX_MNL; + } + + buf[4] |= sink_desc_len; + + for (i = 0; i < sink_desc_len; i++) { + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); + buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + } + } + + for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { + if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) + continue; /* not handled by ATI/AMD */ + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); + ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); + + if (ati_sad <= 0) + continue; + + if (ati_sad & ATI_AUDIODESC_RATES) { + /* format is supported, copy SAD as-is */ + buf[pos++] = (ati_sad & 0x0000ff) >> 0; + buf[pos++] = (ati_sad & 0x00ff00) >> 8; + buf[pos++] = (ati_sad & 0xff0000) >> 16; + } + + if (i == AUDIO_CODING_TYPE_LPCM + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { + /* for PCM there is a separate stereo rate mask */ + buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; + /* rates from the extra byte */ + buf[pos++] = (ati_sad & 0xff000000) >> 24; + buf[pos++] = (ati_sad & 0x00ff0000) >> 16; + } + } + + if (pos == ELD_FIXED_BYTES + sink_desc_len) { + codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); + return -EINVAL; + } + + /* + * HDMI VSDB latency format: + * separately for both audio and video: + * 0 field not valid or unknown latency + * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) + * 255 audio/video not supported + * + * HDA latency format: + * single value indicating video latency relative to audio: + * 0 unknown or 0ms + * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) + * [251..255] reserved + */ + aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); + if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { + int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); + int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8; + + if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb && + video_latency_hdmi > audio_latency_hdmi) + buf[6] = video_latency_hdmi - audio_latency_hdmi; + /* else unknown/invalid or 0ms or video ahead of audio, so use zero */ + } + + /* SAD count */ + buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; + + /* Baseline ELD block length is 4-byte aligned */ + pos = round_up(pos, 4); + + /* Baseline ELD length (4-byte header is not counted in) */ + buf[2] = (pos - 4) / 4; + + *eld_size = pos; + + return 0; +} + +static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, + int dev_id, unsigned char *buf, int *eld_size) +{ + WARN_ON(dev_id != 0); + /* call hda_eld.c ATI/AMD-specific function */ + return get_eld_ati(codec, nid, buf, eld_size, + is_amdhdmi_rev3_or_later(codec)); +} + +static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id, int ca, + int active_channels, int conn_type) +{ + WARN_ON(dev_id != 0); + snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); +} + +static int atihdmi_paired_swap_fc_lfe(int pos) +{ + /* + * ATI/AMD have automatic FC/LFE swap built-in + * when in pairwise mapping mode. + */ + + switch (pos) { + /* see channel_allocations[].speakers[] */ + case 2: return 3; + case 3: return 2; + default: return pos; + } +} + +static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + struct hdac_cea_channel_speaker_allocation *cap; + int i, j; + + /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ + + cap = snd_hdac_get_ch_alloc_from_ca(ca); + for (i = 0; i < chs; ++i) { + int mask = snd_hdac_chmap_to_spk_mask(map[i]); + bool ok = false; + bool companion_ok = false; + + if (!mask) + continue; + + for (j = 0 + i % 2; j < 8; j += 2) { + int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); + + if (cap->speakers[chan_idx] == mask) { + /* channel is in a supported position */ + ok = true; + + if (i % 2 == 0 && i + 1 < chs) { + /* even channel, check the odd companion */ + int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); + int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); + int comp_mask_act = cap->speakers[comp_chan_idx]; + + if (comp_mask_req == comp_mask_act) + companion_ok = true; + else + return -EINVAL; + } + break; + } + } + + if (!ok) + return -EINVAL; + + if (companion_ok) + i++; /* companion channel already checked */ + } + + return 0; +} + +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + int verb; + int ati_channel_setup = 0; + + if (hdmi_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); + + /* In case this is an odd slot but without stream channel, do not + * disable the slot since the corresponding even slot could have a + * channel. In case neither have a channel, the slot pair will be + * disabled when this function is called for the even slot. + */ + if (hdmi_slot % 2 != 0 && stream_channel == 0xf) + return 0; + + hdmi_slot -= hdmi_slot % 2; + + if (stream_channel != 0xf) + stream_channel -= stream_channel % 2; + } + + verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; + + /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ + + if (stream_channel != 0xf) + ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; + + return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); +} + +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) +{ + struct hda_codec *codec = hdac_to_hda_codec(hdac); + bool was_odd = false; + int ati_asp_slot = asp_slot; + int verb; + int ati_channel_setup; + + if (asp_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); + if (ati_asp_slot % 2 != 0) { + ati_asp_slot -= 1; + was_odd = true; + } + } + + verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; + + ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); + + if (!(ati_channel_setup & ATI_OUT_ENABLE)) + return 0xf; + + return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; +} + +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, + int channels) +{ + int c; + + /* + * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so + * we need to take that into account (a single channel may take 2 + * channel slots if we need to carry a silent channel next to it). + * On Rev3+ AMD codecs this function is not used. + */ + int chanpairs = 0; + + /* We only produce even-numbered channel count TLVs */ + if ((channels % 2) != 0) + return -1; + + for (c = 0; c < 7; c += 2) { + if (cap->speakers[c] || cap->speakers[c+1]) + chanpairs++; + } + + if (chanpairs * 2 != channels) + return -1; + + return SNDRV_CTL_TLVT_CHMAP_PAIRED; +} + +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + /* produce paired maps for pre-rev3 ATI/AMD codecs */ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); + int spk = cap->speakers[chan]; + + if (!spk) { + /* add N/A channel if the companion channel is occupied */ + if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) + chmap[count++] = SNDRV_CHMAP_NA; + + continue; + } + + chmap[count++] = snd_hdac_spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, bool hbr) +{ + int hbr_ctl, hbr_ctl_new; + + WARN_ON(dev_id != 0); + + hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); + if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) { + if (hbr) + hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; + else + hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; + + codec_dbg(codec, + "%s: NID=0x%x, %shbr-ctl=0x%x\n", + __func__, + pin_nid, + hbr_ctl == hbr_ctl_new ? "" : "new-", + hbr_ctl_new); + + if (hbr_ctl != hbr_ctl_new) + snd_hda_codec_write(codec, pin_nid, 0, + ATI_VERB_SET_HBR_CONTROL, + hbr_ctl_new); + + } else if (hbr) + return -EINVAL; + + return 0; +} + +static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, + u32 stream_tag, int format) +{ + if (is_amdhdmi_rev3_or_later(codec)) { + int ramp_rate = 180; /* default as per AMD spec */ + /* disable ramp-up/down for non-pcm as per AMD spec */ + if (format & AC_FMT_TYPE_NON_PCM) + ramp_rate = 0; + + snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); + } + + return snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); +} + + +static int atihdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, err; + + err = snd_hda_hdmi_generic_init(codec); + + if (err) + return err; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* make sure downmix information in infoframe is zero */ + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); + + /* enable channel-wise remap mode if supported */ + if (has_amd_full_remap_support(codec)) + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + ATI_VERB_SET_MULTICHANNEL_MODE, + ATI_MULTICHANNEL_MODE_SINGLE); + } + codec->auto_runtime_pm = 1; + + return 0; +} + +/* map from pin NID to port; port is 0-based */ +/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ +static int atihdmi_pin2port(void *audio_ptr, int pin_nid) +{ + return pin_nid / 2 - 1; +} + +/* reverse-map from port to pin NID: see above */ +static int atihdmi_port2pin(struct hda_codec *codec, int port) +{ + return port * 2 + 3; +} + +static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { + .pin2port = atihdmi_pin2port, + .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify, + .master_bind = snd_hda_hdmi_acomp_master_bind, + .master_unbind = snd_hda_hdmi_acomp_master_unbind, +}; + +static int patch_atihdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + struct hdmi_spec_per_cvt *per_cvt; + int err, cvt_idx; + + err = patch_generic_hdmi(codec); + + if (err) + return err; + + codec->patch_ops.init = atihdmi_init; + + spec = codec->spec; + + spec->static_pcm_mapping = true; + + spec->ops.pin_get_eld = atihdmi_pin_get_eld; + spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; + spec->ops.setup_stream = atihdmi_setup_stream; + + spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; + + if (!has_amd_full_remap_support(codec)) { + /* override to ATI/AMD-specific versions with pairwise mapping */ + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + atihdmi_paired_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + } + + /* ATI/AMD converters do not advertise all of their capabilities */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->channels_max = max(per_cvt->channels_max, 8u); + per_cvt->rates |= SUPPORTED_RATES; + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } + + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); + + /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing + * the link-down as is. Tell the core to allow it. + */ + codec->link_down_at_suspend = 1; + + snd_hda_hdmi_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin); + + return 0; +} + +/* + * patch entries + */ +static const struct hda_device_id snd_hda_id_atihdmi[] = { +HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), +HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), +{} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_atihdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD/ATI HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver atihdmi_driver = { + .id = snd_hda_id_atihdmi, +}; + +module_hda_codec_driver(atihdmi_driver); diff --git a/sound/hda/codecs/hdmi/eld.c b/sound/hda/codecs/hdmi/eld.c index d3e87b9c1a4f1..1464fd1c675bc 100644 --- a/sound/hda/codecs/hdmi/eld.c +++ b/sound/hda/codecs/hdmi/eld.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "hda_local.h" @@ -229,174 +228,3 @@ void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, hinfo->maxbps = min(hinfo->maxbps, maxbps); hinfo->channels_max = min(hinfo->channels_max, channels_max); } - - -/* ATI/AMD specific stuff (ELD emulation) */ - -#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 -#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 -#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 -#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 -#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b -#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 -#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 - -#define ATI_SPKALLOC_SPKALLOC 0x007f -#define ATI_SPKALLOC_TYPE_HDMI 0x0100 -#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 - -/* first three bytes are just standard SAD */ -#define ATI_AUDIODESC_CHANNELS 0x00000007 -#define ATI_AUDIODESC_RATES 0x0000ff00 -#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 - -/* in standard HDMI VSDB format */ -#define ATI_DELAY_VIDEO_LATENCY 0x000000ff -#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 - -enum ati_sink_info_idx { - ATI_INFO_IDX_MANUFACTURER_ID = 0, - ATI_INFO_IDX_PRODUCT_ID = 1, - ATI_INFO_IDX_SINK_DESC_LEN = 2, - ATI_INFO_IDX_PORT_ID_LOW = 3, - ATI_INFO_IDX_PORT_ID_HIGH = 4, - ATI_INFO_IDX_SINK_DESC_FIRST = 5, - ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ -}; - -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, bool rev3_or_later) -{ - int spkalloc, ati_sad, aud_synch; - int sink_desc_len = 0; - int pos, i; - - /* ATI/AMD does not have ELD, emulate it */ - - spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); - - if (spkalloc <= 0) { - codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n"); - return -EINVAL; - } - - memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); - - /* version */ - buf[0] = ELD_VER_CEA_861D << 3; - - /* speaker allocation from EDID */ - buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; - - /* is DisplayPort? */ - if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) - buf[5] |= 0x04; - - pos = ELD_FIXED_BYTES; - - if (rev3_or_later) { - int sink_info; - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 8); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le32(sink_info, buf + 12); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 16); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); - sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - put_unaligned_le16(sink_info, buf + 18); - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); - sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - - if (sink_desc_len > ELD_MAX_MNL) { - codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", - sink_desc_len); - sink_desc_len = ELD_MAX_MNL; - } - - buf[4] |= sink_desc_len; - - for (i = 0; i < sink_desc_len; i++) { - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); - buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); - } - } - - for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { - if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) - continue; /* not handled by ATI/AMD */ - - snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); - ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); - - if (ati_sad <= 0) - continue; - - if (ati_sad & ATI_AUDIODESC_RATES) { - /* format is supported, copy SAD as-is */ - buf[pos++] = (ati_sad & 0x0000ff) >> 0; - buf[pos++] = (ati_sad & 0x00ff00) >> 8; - buf[pos++] = (ati_sad & 0xff0000) >> 16; - } - - if (i == AUDIO_CODING_TYPE_LPCM - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) - && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { - /* for PCM there is a separate stereo rate mask */ - buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; - /* rates from the extra byte */ - buf[pos++] = (ati_sad & 0xff000000) >> 24; - buf[pos++] = (ati_sad & 0x00ff0000) >> 16; - } - } - - if (pos == ELD_FIXED_BYTES + sink_desc_len) { - codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); - return -EINVAL; - } - - /* - * HDMI VSDB latency format: - * separately for both audio and video: - * 0 field not valid or unknown latency - * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) - * 255 audio/video not supported - * - * HDA latency format: - * single value indicating video latency relative to audio: - * 0 unknown or 0ms - * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) - * [251..255] reserved - */ - aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); - if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { - int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); - int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8; - - if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb && - video_latency_hdmi > audio_latency_hdmi) - buf[6] = video_latency_hdmi - audio_latency_hdmi; - /* else unknown/invalid or 0ms or video ahead of audio, so use zero */ - } - - /* SAD count */ - buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; - - /* Baseline ELD block length is 4-byte aligned */ - pos = round_up(pos, 4); - - /* Baseline ELD length (4-byte header is not counted in) */ - buf[2] = (pos - 4) / 4; - - *eld_size = pos; - - return 0; -} diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 3811eb1dc9989..85aaa454072fc 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -33,6 +33,7 @@ #include "hda_local.h" #include "hda_jack.h" #include "hda_controller.h" +#include "hdmi_local.h" static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); @@ -42,214 +43,12 @@ static bool enable_acomp = true; module_param(enable_acomp, bool, 0444); MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); -static bool enable_silent_stream = -IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); -module_param(enable_silent_stream, bool, 0644); -MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); - static bool enable_all_pins; module_param(enable_all_pins, bool, 0444); MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins"); -struct hdmi_spec_per_cvt { - hda_nid_t cvt_nid; - bool assigned; /* the stream has been assigned */ - bool silent_stream; /* silent stream activated */ - unsigned int channels_min; - unsigned int channels_max; - u32 rates; - u64 formats; - unsigned int maxbps; -}; - -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - -struct hdmi_spec_per_pin { - hda_nid_t pin_nid; - int dev_id; - /* pin idx, different device entries on the same pin use the same idx */ - int pin_nid_idx; - int num_mux_nids; - hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; - int mux_idx; - hda_nid_t cvt_nid; - - struct hda_codec *codec; - struct hdmi_eld sink_eld; - struct mutex lock; - struct delayed_work work; - struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ - int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ - int prev_pcm_idx; /* previously assigned pcm index */ - int repoll_count; - bool setup; /* the stream has been set up by prepare callback */ - bool silent_stream; - int channels; /* current number of channels */ - bool non_pcm; - bool chmap_set; /* channel-map override by ALSA API? */ - unsigned char chmap[8]; /* ALSA API channel-map */ -#ifdef CONFIG_SND_PROC_FS - struct snd_info_entry *proc_entry; -#endif -}; - -/* operations used by generic code that can be overridden by patches */ -struct hdmi_ops { - int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, unsigned char *buf, int *eld_size); - - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, - int ca, int active_channels, int conn_type); - - /* enable/disable HBR (HD passthrough) */ - int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr); - - int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format); - - void (*pin_cvt_fixup)(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid); -}; - -struct hdmi_pcm { - struct hda_pcm *pcm; - struct snd_jack *jack; - struct snd_kcontrol *eld_ctl; -}; - -enum { - SILENT_STREAM_OFF = 0, - SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ - SILENT_STREAM_I915, /* Intel i915 extension */ -}; - -struct hdmi_spec { - struct hda_codec *codec; - int num_cvts; - struct snd_array cvts; /* struct hdmi_spec_per_cvt */ - hda_nid_t cvt_nids[4]; /* only for haswell fix */ - - /* - * num_pins is the number of virtual pins - * for example, there are 3 pins, and each pin - * has 4 device entries, then the num_pins is 12 - */ - int num_pins; - /* - * num_nids is the number of real pins - * In the above example, num_nids is 3 - */ - int num_nids; - /* - * dev_num is the number of device entries - * on each pin. - * In the above example, dev_num is 4 - */ - int dev_num; - struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hdmi_pcm pcm_rec[8]; - struct mutex pcm_lock; - struct mutex bind_lock; /* for audio component binding */ - /* pcm_bitmap means which pcms have been assigned to pins*/ - unsigned long pcm_bitmap; - int pcm_used; /* counter of pcm_rec[] */ - /* bitmap shows whether the pcm is opened in user space - * bit 0 means the first playback PCM (PCM3); - * bit 1 means the second playback PCM, and so on. - */ - unsigned long pcm_in_use; - - struct hdmi_eld temp_eld; - struct hdmi_ops ops; - - bool dyn_pin_out; - bool static_pcm_mapping; - /* hdmi interrupt trigger control flag for Nvidia codec */ - bool hdmi_intr_trig_ctrl; - bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */ - - bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ - /* - * Non-generic VIA/NVIDIA specific - */ - struct hda_multi_out multiout; - struct hda_pcm_stream pcm_playback; - - bool use_acomp_notifier; /* use eld_notify callback for hotplug */ - bool acomp_registered; /* audio component registered in this driver */ - bool force_connect; /* force connectivity */ - struct drm_audio_component_audio_ops drm_audio_ops; - int (*port2pin)(struct hda_codec *, int); /* reverse port/pin mapping */ - - struct hdac_chmap chmap; - hda_nid_t vendor_nid; - const int *port_map; - int port_num; - int silent_stream_type; -}; - -#ifdef CONFIG_SND_HDA_COMPONENT -static inline bool codec_has_acomp(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - return spec->use_acomp_notifier; -} -#else -#define codec_has_acomp(codec) false -#endif - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; - - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -struct dp_audio_infoframe { - u8 type; /* 0x84 */ - u8 len; /* 0x1b */ - u8 ver; /* 0x11 << 2 */ - - u8 CC02_CT47; /* match with HDMI infoframe from this on */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -union audio_infoframe { - struct hdmi_audio_infoframe hdmi; - struct dp_audio_infoframe dp; - DECLARE_FLEX_ARRAY(u8, bytes); -}; - -/* - * HDMI routines - */ - -#define get_pin(spec, idx) \ - ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) -#define get_cvt(spec, idx) \ - ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -/* obtain hdmi_pcm object assigned to idx */ -#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) -/* obtain hda_pcm object assigned to idx */ -#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) - -static int pin_id_to_pin_index(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id) +int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id) { struct hdmi_spec *spec = codec->spec; int pin_idx; @@ -272,6 +71,7 @@ static int pin_id_to_pin_index(struct hda_codec *codec, codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); return -EINVAL; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_pin_id_to_pin_index, "SND_HDA_CODEC_HDMI"); static int hinfo_to_pcm_index(struct hda_codec *codec, struct hda_pcm_stream *hinfo) @@ -735,9 +535,9 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, } } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool non_pcm) +void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm) { struct hdmi_spec *spec = codec->spec; struct hdac_chmap *chmap = &spec->chmap; @@ -783,6 +583,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, per_pin->non_pcm = non_pcm; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_audio_infoframe, "SND_HDA_CODEC_HDMI"); /* * Unsolicited events @@ -790,8 +591,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll); -static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, - int dev_id) +void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec, + hda_nid_t nid, int dev_id) { struct hdmi_spec *spec = codec->spec; int pin_idx = pin_id_to_pin_index(codec, nid, dev_id); @@ -802,6 +603,8 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid, hdmi_present_sense(get_pin(spec, pin_idx), 1); mutex_unlock(&spec->pcm_lock); } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_check_presence_and_report, + "SND_HDA_CODEC_HDMI"); static void jack_callback(struct hda_codec *codec, struct hda_jack_callback *jack) @@ -810,7 +613,7 @@ static void jack_callback(struct hda_codec *codec, if (codec_has_acomp(codec)) return; - check_presence_and_report(codec, jack->nid, jack->dev_id); + snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id); } static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, @@ -823,7 +626,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); - check_presence_and_report(codec, jack->nid, jack->dev_id); + snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id); } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -850,8 +653,7 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) } } - -static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) +void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res) { int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; @@ -879,27 +681,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) else hdmi_non_intrinsic_event(codec, res); } - -static void haswell_verify_D0(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t nid) -{ - int pwr; - - /* For Haswell, the converter 1/2 may keep in D3 state after bootup, - * thus pins could only choose converter 0 for use. Make sure the - * converters are in correct power state */ - if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - msleep(40); - pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); - pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; - codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); - } -} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_unsol_event, "SND_HDA_CODEC_HDMI"); /* * Callbacks @@ -944,7 +726,8 @@ static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, return 0; } -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, +int snd_hda_hdmi_setup_stream(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format) { @@ -983,6 +766,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_stream, "SND_HDA_CODEC_HDMI"); /* Try to find an available converter * If pin_idx is less then zero, just try to find an available converter. @@ -1046,137 +830,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec, return 0; } -/* Assure the pin select the right convetor */ -static void intel_verify_pin_cvt_connect(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - hda_nid_t pin_nid = per_pin->pin_nid; - int mux_idx, curr; - - mux_idx = per_pin->mux_idx; - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) - snd_hda_codec_write_cache(codec, pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); -} - -/* get the mux index for the converter of the pins - * converter's mux index is the same for all pins on Intel platform - */ -static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, - hda_nid_t cvt_nid) -{ - int i; - - for (i = 0; i < spec->num_cvts; i++) - if (spec->cvt_nids[i] == cvt_nid) - return i; - return -EINVAL; -} - -/* Intel HDMI workaround to fix audio routing issue: - * For some Intel display codecs, pins share the same connection list. - * So a conveter can be selected by multiple pins and playback on any of these - * pins will generate sound on the external display, because audio flows from - * the same converter to the display pipeline. Also muting one pin may make - * other pins have no sound output. - * So this function assures that an assigned converter for a pin is not selected - * by any other pins. - */ -static void intel_not_share_assigned_cvt(struct hda_codec *codec, - hda_nid_t pin_nid, - int dev_id, int mux_idx) -{ - struct hdmi_spec *spec = codec->spec; - hda_nid_t nid; - int cvt_idx, curr; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - int pin_idx; - - /* configure the pins connections */ - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - int dev_id_saved; - int dev_num; - - per_pin = get_pin(spec, pin_idx); - /* - * pin not connected to monitor - * no need to operate on it - */ - if (!per_pin->pcm) - continue; - - if ((per_pin->pin_nid == pin_nid) && - (per_pin->dev_id == dev_id)) - continue; - - /* - * if per_pin->dev_id >= dev_num, - * snd_hda_get_dev_select() will fail, - * and the following operation is unpredictable. - * So skip this situation. - */ - dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; - if (per_pin->dev_id >= dev_num) - continue; - - nid = per_pin->pin_nid; - - /* - * Calling this function should not impact - * on the device entry selection - * So let's save the dev id for each pin, - * and restore it when return - */ - dev_id_saved = snd_hda_get_dev_select(codec, nid); - snd_hda_set_dev_select(codec, nid, per_pin->dev_id); - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (curr != mux_idx) { - snd_hda_set_dev_select(codec, nid, dev_id_saved); - continue; - } - - - /* choose an unassigned converter. The conveters in the - * connection list are in the same order as in the codec. - */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - if (!per_cvt->assigned) { - codec_dbg(codec, - "choose cvt %d for pin NID 0x%x\n", - cvt_idx, nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - cvt_idx); - break; - } - } - snd_hda_set_dev_select(codec, nid, dev_id_saved); - } -} - -/* A wrapper of intel_not_share_asigned_cvt() */ -static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) -{ - int mux_idx; - struct hdmi_spec *spec = codec->spec; - - /* On Intel platform, the mapping of converter nid to - * mux index of the pins are always the same. - * The pin nid may be 0, this means all pins will not - * share the converter. - */ - mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); - if (mux_idx >= 0) - intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); -} - /* skeleton caller of pin_cvt_fixup ops */ static void pin_cvt_fixup(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, @@ -1466,7 +1119,7 @@ static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, per_pin->setup = true; per_pin->mux_idx = mux_idx; - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); } static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, @@ -1583,7 +1236,7 @@ static void update_eld(struct hda_codec *codec, */ if (eld->eld_valid && !old_eld_valid && per_pin->setup) { pin_cvt_fixup(codec, per_pin, 0); - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } if (eld_changed && pcm_idx >= 0) @@ -1653,57 +1306,12 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, snd_hda_power_down_pm(codec); } -#define I915_SILENT_RATE 48000 -#define I915_SILENT_CHANNELS 2 -#define I915_SILENT_FORMAT_BITS 16 -#define I915_SILENT_FMT_MASK 0xf - -static void silent_stream_enable_i915(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) -{ - unsigned int format; - - snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, - per_pin->dev_id, I915_SILENT_RATE); - - /* trigger silent stream generation in hw */ - format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, - I915_SILENT_RATE); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, - I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); - usleep_range(100, 200); - snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); - - per_pin->channels = I915_SILENT_CHANNELS; - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); -} - -static void silent_stream_set_kae(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool enable) -{ - unsigned int param; - - codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); - param = (param >> 16) & 0xff; - - if (enable) - param |= AC_DIG3_KAE; - else - param &= ~AC_DIG3_KAE; - - snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); -} - static void silent_stream_enable(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_cvt *per_cvt; int cvt_idx, pin_idx, err; - int keep_power = 0; /* * Power-up will call hdmi_present_sense, so the PM calls @@ -1749,24 +1357,12 @@ static void silent_stream_enable(struct hda_codec *codec, /* configure unused pins to choose other converters */ pin_cvt_fixup(codec, per_pin, 0); - switch (spec->silent_stream_type) { - case SILENT_STREAM_KAE: - silent_stream_enable_i915(codec, per_pin); - silent_stream_set_kae(codec, per_pin, true); - break; - case SILENT_STREAM_I915: - silent_stream_enable_i915(codec, per_pin); - keep_power = 1; - break; - default: - break; - } + spec->ops.silent_stream(codec, per_pin, true); unlock_out: mutex_unlock(&per_pin->lock); - if (err || !keep_power) - snd_hda_power_down_pm(codec); + snd_hda_power_down_pm(codec); } static void silent_stream_disable(struct hda_codec *codec, @@ -1798,12 +1394,7 @@ static void silent_stream_disable(struct hda_codec *codec, per_cvt->silent_stream = false; } - if (spec->silent_stream_type == SILENT_STREAM_I915) { - /* release ref taken in silent_stream_enable() */ - snd_hda_power_down_pm(codec); - } else if (spec->silent_stream_type == SILENT_STREAM_KAE) { - silent_stream_set_kae(codec, per_pin, false); - } + spec->ops.silent_stream(codec, per_pin, false); per_pin->cvt_nid = 0; per_pin->silent_stream = false; @@ -2003,7 +1594,7 @@ static const struct snd_pci_quirk force_connect_list[] = { {} }; -static int hdmi_parse_codec(struct hda_codec *codec) +int snd_hda_hdmi_parse_codec(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; hda_nid_t start_nid; @@ -2056,6 +1647,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_parse_codec, "SND_HDA_CODEC_HDMI"); /* */ @@ -2082,11 +1674,11 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) * HDMI callbacks */ -static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; @@ -2140,7 +1732,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stripe); } - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); if (spec->dyn_pin_out) { snd_hda_set_dev_select(codec, per_pin->pin_nid, @@ -2159,14 +1751,16 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, mutex_unlock(&spec->pcm_lock); return err; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_prepare, "SND_HDA_CODEC_HDMI"); -static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_cleanup, "SND_HDA_CODEC_HDMI"); static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -2237,8 +1831,8 @@ unlock: static const struct hda_pcm_ops generic_ops = { .open = hdmi_pcm_open, .close = hdmi_pcm_close, - .prepare = generic_hdmi_playback_pcm_prepare, - .cleanup = generic_hdmi_playback_pcm_cleanup, + .prepare = snd_hda_hdmi_generic_pcm_prepare, + .cleanup = snd_hda_hdmi_generic_pcm_cleanup, }; static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) @@ -2280,7 +1874,7 @@ static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); mutex_unlock(&per_pin->lock); } @@ -2293,7 +1887,7 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) return per_pin ? true:false; } -static int generic_hdmi_build_pcms(struct hda_codec *codec) +int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int idx, pcm_num; @@ -2332,6 +1926,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_pcms, "SND_HDA_CODEC_HDMI"); static void free_hdmi_jack_priv(struct snd_jack *jack) { @@ -2362,7 +1957,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) return 0; } -static int generic_hdmi_build_controls(struct hda_codec *codec) +int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int dev, err; @@ -2425,8 +2020,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_controls, "SND_HDA_CODEC_HDMI"); -static int generic_hdmi_init_per_pins(struct hda_codec *codec) +int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx; @@ -2441,8 +2037,9 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) } return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init_per_pins, "SND_HDA_CODEC_HDMI"); -static int generic_hdmi_init(struct hda_codec *codec) +int snd_hda_hdmi_generic_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx; @@ -2463,6 +2060,7 @@ static int generic_hdmi_init(struct hda_codec *codec) mutex_unlock(&spec->bind_lock); return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init, "SND_HDA_CODEC_HDMI"); static void hdmi_array_init(struct hdmi_spec *spec, int nums) { @@ -2476,7 +2074,7 @@ static void hdmi_array_free(struct hdmi_spec *spec) snd_array_free(&spec->cvts); } -static void generic_spec_free(struct hda_codec *codec) +void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2487,8 +2085,9 @@ static void generic_spec_free(struct hda_codec *codec) } codec->dp_mst = false; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_spec_free, "SND_HDA_CODEC_HDMI"); -static void generic_hdmi_free(struct hda_codec *codec) +void snd_hda_hdmi_generic_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx, pcm_idx; @@ -2512,10 +2111,11 @@ static void generic_hdmi_free(struct hda_codec *codec) snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack); } - generic_spec_free(codec); + snd_hda_hdmi_generic_spec_free(codec); } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_free, "SND_HDA_CODEC_HDMI"); -static int generic_hdmi_suspend(struct hda_codec *codec) +int snd_hda_hdmi_generic_suspend(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx; @@ -2526,8 +2126,9 @@ static int generic_hdmi_suspend(struct hda_codec *codec) } return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_suspend, "SND_HDA_CODEC_HDMI"); -static int generic_hdmi_resume(struct hda_codec *codec) +int snd_hda_hdmi_generic_resume(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx; @@ -2541,26 +2142,27 @@ static int generic_hdmi_resume(struct hda_codec *codec) } return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_resume, "SND_HDA_CODEC_HDMI"); static const struct hda_codec_ops generic_hdmi_patch_ops = { - .init = generic_hdmi_init, - .free = generic_hdmi_free, - .build_pcms = generic_hdmi_build_pcms, - .build_controls = generic_hdmi_build_controls, - .unsol_event = hdmi_unsol_event, - .suspend = generic_hdmi_suspend, - .resume = generic_hdmi_resume, + .init = snd_hda_hdmi_generic_init, + .free = snd_hda_hdmi_generic_free, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, }; static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = hdmi_pin_get_eld, .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, - .setup_stream = hdmi_setup_stream, + .setup_stream = snd_hda_hdmi_setup_stream, }; /* allocate codec->spec and assign/initialize generic parser ops */ -static int alloc_generic_hdmi(struct hda_codec *codec) +int snd_hda_hdmi_generic_alloc(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -2587,25 +2189,27 @@ static int alloc_generic_hdmi(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_alloc, "SND_HDA_CODEC_HDMI"); /* generic HDMI parser */ -static int patch_generic_hdmi(struct hda_codec *codec) +int patch_generic_hdmi(struct hda_codec *codec) { int err; - err = alloc_generic_hdmi(codec); + err = snd_hda_hdmi_generic_alloc(codec); if (err < 0) return err; - err = hdmi_parse_codec(codec); + err = snd_hda_hdmi_parse_codec(codec); if (err < 0) { - generic_spec_free(codec); + snd_hda_hdmi_generic_spec_free(codec); return err; } - generic_hdmi_init_per_pins(codec); + snd_hda_hdmi_generic_init_per_pins(codec); return 0; } +EXPORT_SYMBOL_NS_GPL(patch_generic_hdmi, "SND_HDA_CODEC_HDMI"); /* * generic audio component binding @@ -2650,18 +2254,20 @@ static void generic_acomp_notifier_set(struct drm_audio_component *acomp, } /* enable / disable the notifier via master bind / unbind */ -static int generic_acomp_master_bind(struct device *dev, - struct drm_audio_component *acomp) +int snd_hda_hdmi_acomp_master_bind(struct device *dev, + struct drm_audio_component *acomp) { generic_acomp_notifier_set(acomp, true); return 0; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_bind, "SND_HDA_CODEC_HDMI"); -static void generic_acomp_master_unbind(struct device *dev, - struct drm_audio_component *acomp) +void snd_hda_hdmi_acomp_master_unbind(struct device *dev, + struct drm_audio_component *acomp) { generic_acomp_notifier_set(acomp, false); } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_unbind, "SND_HDA_CODEC_HDMI"); /* check whether both HD-audio and DRM PCI devices belong to the same bus */ static int match_bound_vga(struct device *dev, int subtype, void *data) @@ -2677,7 +2283,7 @@ static int match_bound_vga(struct device *dev, int subtype, void *data) } /* audio component notifier for AMD/Nvidia HDMI codecs */ -static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) +void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) { struct hda_codec *codec = audio_ptr; struct hdmi_spec *spec = codec->spec; @@ -2693,12 +2299,13 @@ static void generic_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) return; - check_presence_and_report(codec, pin_nid, dev_id); + snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_pin_eld_notify, "SND_HDA_CODEC_HDMI"); /* set up the private drm_audio_ops from the template */ -static void setup_drm_audio_ops(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops) +void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops) { struct hdmi_spec *spec = codec->spec; @@ -2713,11 +2320,12 @@ static void setup_drm_audio_ops(struct hda_codec *codec, spec->drm_audio_ops.master_bind = ops->master_bind; spec->drm_audio_ops.master_unbind = ops->master_unbind; } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_drm_audio_ops, "SND_HDA_CODEC_HDMI"); /* initialize the generic HDMI audio component */ -static void generic_acomp_init(struct hda_codec *codec, - const struct drm_audio_component_audio_ops *ops, - int (*port2pin)(struct hda_codec *, int)) +void snd_hda_hdmi_acomp_init(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops, + int (*port2pin)(struct hda_codec *, int)) { struct hdmi_spec *spec = codec->spec; @@ -2727,1766 +2335,16 @@ static void generic_acomp_init(struct hda_codec *codec, } spec->port2pin = port2pin; - setup_drm_audio_ops(codec, ops); + snd_hda_hdmi_setup_drm_audio_ops(codec, ops); if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, match_bound_vga, 0)) { spec->acomp_registered = true; } } +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI"); /* - * Intel codec parsers and helpers - */ - -#define INTEL_GET_VENDOR_VERB 0xf81 -#define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ -#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ - -static void intel_haswell_enable_all_pins(struct hda_codec *codec, - bool update_tree) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) - return; - - vendor_param |= INTEL_EN_ALL_PIN_CVTS; - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); - if (vendor_param == -1) - return; - - if (update_tree) - snd_hda_codec_update_widgets(codec); -} - -static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) -{ - unsigned int vendor_param; - struct hdmi_spec *spec = codec->spec; - - vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, - INTEL_GET_VENDOR_VERB, 0); - if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) - return; - - /* enable DP1.2 mode */ - vendor_param |= INTEL_EN_DP12; - snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); - snd_hda_codec_write_cache(codec, spec->vendor_nid, 0, - INTEL_SET_VENDOR_VERB, vendor_param); -} - -/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. - * Otherwise you may get severe h/w communication errors. - */ -static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - if (power_state == AC_PWRST_D0) { - intel_haswell_enable_all_pins(codec, false); - intel_haswell_fixup_enable_dp12(codec); - } - - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - snd_hda_codec_set_power_to_all(codec, fg, power_state); -} - -/* There is a fixed mapping between audio pin node and display port. - * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: - * Pin Widget 5 - PORT B (port = 1 in i915 driver) - * Pin Widget 6 - PORT C (port = 2 in i915 driver) - * Pin Widget 7 - PORT D (port = 3 in i915 driver) - * - * on VLV, ILK: - * Pin Widget 4 - PORT B (port = 1 in i915 driver) - * Pin Widget 5 - PORT C (port = 2 in i915 driver) - * Pin Widget 6 - PORT D (port = 3 in i915 driver) */ -static int intel_base_nid(struct hda_codec *codec) -{ - switch (codec->core.vendor_id) { - case 0x80860054: /* ILK */ - case 0x80862804: /* ILK */ - case 0x80862882: /* VLV */ - return 4; - default: - return 5; - } -} - -static int intel_pin2port(void *audio_ptr, int pin_nid) -{ - struct hda_codec *codec = audio_ptr; - struct hdmi_spec *spec = codec->spec; - int base_nid, i; - - if (!spec->port_num) { - base_nid = intel_base_nid(codec); - if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) - return -1; - return pin_nid - base_nid + 1; - } - - /* - * looking for the pin number in the mapping table and return - * the index which indicate the port number - */ - for (i = 0; i < spec->port_num; i++) { - if (pin_nid == spec->port_map[i]) - return i; - } - - codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); - return -1; -} - -static int intel_port2pin(struct hda_codec *codec, int port) -{ - struct hdmi_spec *spec = codec->spec; - - if (!spec->port_num) { - /* we assume only from port-B to port-D */ - if (port < 1 || port > 3) - return 0; - return port + intel_base_nid(codec) - 1; - } - - if (port < 0 || port >= spec->port_num) - return 0; - return spec->port_map[port]; -} - -static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) -{ - struct hda_codec *codec = audio_ptr; - int pin_nid; - int dev_id = pipe; - - pin_nid = intel_port2pin(codec, port); - if (!pin_nid) - return; - /* skip notification during system suspend (but not in runtime PM); - * the state will be updated at resume - */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) - return; - - snd_hdac_i915_set_bclk(&codec->bus->core); - check_presence_and_report(codec, pin_nid, dev_id); -} - -static const struct drm_audio_component_audio_ops intel_audio_ops = { - .pin2port = intel_pin2port, - .pin_eld_notify = intel_pin_eld_notify, -}; - -/* register i915 component pin_eld_notify callback */ -static void register_i915_notifier(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - spec->use_acomp_notifier = true; - spec->port2pin = intel_port2pin; - setup_drm_audio_ops(codec, &intel_audio_ops); - snd_hdac_acomp_register_notifier(&codec->bus->core, - &spec->drm_audio_ops); - /* no need for forcible resume for jack check thanks to notifier */ - codec->relaxed_resume = 1; -} - -/* setup_stream ops override for HSW+ */ -static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, u32 stream_tag, - int format) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); - struct hdmi_spec_per_pin *per_pin; - int res; - - if (pin_idx < 0) - per_pin = NULL; - else - per_pin = get_pin(spec, pin_idx); - - haswell_verify_D0(codec, cvt_nid, pin_nid); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - silent_stream_set_kae(codec, per_pin, false); - /* wait for pending transfers in codec to clear */ - usleep_range(100, 200); - } - - res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); - - if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { - usleep_range(100, 200); - silent_stream_set_kae(codec, per_pin, true); - } - - return res; -} - -/* pin_cvt_fixup ops override for HSW+ and VLV+ */ -static void i915_pin_cvt_fixup(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - hda_nid_t cvt_nid) -{ - if (per_pin) { - haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid); - snd_hda_set_dev_select(codec, per_pin->pin_nid, - per_pin->dev_id); - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, - per_pin->dev_id, per_pin->mux_idx); - } else { - intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid); - } -} - -static int i915_adlp_hdmi_suspend(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - bool silent_streams = false; - int pin_idx, res; - - res = generic_hdmi_suspend(codec); - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - if (per_pin->silent_stream) { - silent_streams = true; - break; - } - } - - if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) { - /* - * stream-id should remain programmed when codec goes - * to runtime suspend - */ - codec->no_stream_clean_at_suspend = 1; - - /* - * the system might go to S3, in which case keep-alive - * must be reprogrammed upon resume - */ - codec->forced_resume = 1; - - codec_dbg(codec, "HDMI: KAE active at suspend\n"); - } else { - codec->no_stream_clean_at_suspend = 0; - codec->forced_resume = 0; - } - - return res; -} - -static int i915_adlp_hdmi_resume(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, res; - - res = generic_hdmi_resume(codec); - - /* KAE not programmed at suspend, nothing to do here */ - if (!codec->no_stream_clean_at_suspend) - return res; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* - * If system was in suspend with monitor connected, - * the codec setting may have been lost. Re-enable - * keep-alive. - */ - if (per_pin->silent_stream) { - unsigned int param; - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_CONV, 0); - if (!param) { - codec_dbg(codec, "HDMI: KAE: restore stream id\n"); - silent_stream_enable_i915(codec, per_pin); - } - - param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); - if (!(param & (AC_DIG3_KAE << 16))) { - codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); - silent_stream_set_kae(codec, per_pin, true); - } - } - } - - return res; -} - -/* precondition and allocation for Intel codecs */ -static int alloc_intel_hdmi(struct hda_codec *codec) -{ - int err; - - /* requires i915 binding */ - if (!codec->bus->core.audio_component) { - codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); - /* set probe_id here to prevent generic fallback binding */ - codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; - return -ENODEV; - } - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - /* no need to handle unsol events */ - codec->patch_ops.unsol_event = NULL; - return 0; -} - -/* parse and post-process for Intel codecs */ -static int parse_intel_hdmi(struct hda_codec *codec) -{ - int err, retries = 3; - - do { - err = hdmi_parse_codec(codec); - } while (err < 0 && retries--); - - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - register_i915_notifier(codec); - return 0; -} - -/* Intel Haswell and onwards; audio component with eld notifier */ -static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, - const int *port_map, int port_num, int dev_num, - bool send_silent_stream) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - codec->dp_mst = true; - spec->vendor_nid = vendor_nid; - spec->port_map = port_map; - spec->port_num = port_num; - spec->intel_hsw_fixup = true; - spec->dev_num = dev_num; - - intel_haswell_enable_all_pins(codec, true); - intel_haswell_fixup_enable_dp12(codec); - - codec->display_power_control = 1; - - codec->patch_ops.set_power_state = haswell_set_power_state; - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.setup_stream = i915_hsw_setup_stream; - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - /* - * Enable silent stream feature, if it is enabled via - * module param or Kconfig option - */ - if (send_silent_stream) - spec->silent_stream_type = SILENT_STREAM_I915; - - return parse_intel_hdmi(codec); -} - -static int patch_i915_hsw_hdmi(struct hda_codec *codec) -{ - return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, - enable_silent_stream); -} - -static int patch_i915_glk_hdmi(struct hda_codec *codec) -{ - /* - * Silent stream calls audio component .get_power() from - * .pin_eld_notify(). On GLK this will deadlock in i915 due - * to the audio vs. CDCLK workaround. - */ - return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); -} - -static int patch_i915_icl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, - enable_silent_stream); -} - -static int patch_i915_tgl_hdmi(struct hda_codec *codec) -{ - /* - * pin to port mapping table where the value indicate the pin number and - * the index indicate the port number. - */ - static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - - return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, - enable_silent_stream); -} - -static int patch_i915_adlp_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int res; - - res = patch_i915_tgl_hdmi(codec); - if (!res) { - spec = codec->spec; - - if (spec->silent_stream_type) { - spec->silent_stream_type = SILENT_STREAM_KAE; - - codec->patch_ops.resume = i915_adlp_hdmi_resume; - codec->patch_ops.suspend = i915_adlp_hdmi_suspend; - } - } - - return res; -} - -/* Intel Baytrail and Braswell; with eld notifier */ -static int patch_i915_byt_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - spec = codec->spec; - - /* For Valleyview/Cherryview, only the display codec is in the display - * power well and can use link_power ops to request/release the power. - */ - codec->display_power_control = 1; - - codec->depop_delay = 0; - codec->auto_runtime_pm = 1; - - spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; - - return parse_intel_hdmi(codec); -} - -/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ -static int patch_i915_cpt_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; - return parse_intel_hdmi(codec); -} - -/* - * Shared non-generic implementations - */ - -static int simple_playback_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - unsigned int chans; - struct hda_pcm_stream *pstr; - struct hdmi_spec_per_cvt *per_cvt; - - per_cvt = get_cvt(spec, 0); - chans = get_wcaps(codec, per_cvt->cvt_nid); - chans = get_wcaps_channels(chans); - - info = snd_hda_codec_pcm_new(codec, "HDMI 0"); - if (!info) - return -ENOMEM; - spec->pcm_rec[0].pcm = info; - info->pcm_type = HDA_PCM_TYPE_HDMI; - pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; - *pstr = spec->pcm_playback; - pstr->nid = per_cvt->cvt_nid; - if (pstr->channels_max <= 2 && chans && chans <= 16) - pstr->channels_max = chans; - - return 0; -} - -/* unsolicited event for jack sensing */ -static void simple_hdmi_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_report_sync(codec); -} - -/* generic_hdmi_build_jack can be used for simple_hdmi, too, - * as long as spec->pins[] is set correctly - */ -#define simple_hdmi_build_jack generic_hdmi_build_jack - -static int simple_playback_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt; - int err; - - per_cvt = get_cvt(spec, 0); - err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid, - per_cvt->cvt_nid, - HDA_PCM_TYPE_HDMI); - if (err < 0) - return err; - return simple_hdmi_build_jack(codec, 0); -} - -static int simple_playback_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); - hda_nid_t pin = per_pin->pin_nid; - - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - /* some codecs require to unmute the pin */ - if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id); - return 0; -} - -static void simple_playback_free(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - - hdmi_array_free(spec); - kfree(spec); -} - -/* - * Nvidia specific implementations - */ - -#define Nv_VERB_SET_Channel_Allocation 0xF79 -#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A -#define Nv_VERB_SET_Audio_Protection_On 0xF98 -#define Nv_VERB_SET_Audio_Protection_Off 0xF99 - -#define nvhdmi_master_con_nid_7x 0x04 -#define nvhdmi_master_pin_nid_7x 0x05 - -static const hda_nid_t nvhdmi_con_nids_7x[4] = { - /*front, rear, clfe, rear_surr */ - 0x6, 0x8, 0xa, 0xc, -}; - -static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { - /* set audio protect on */ - { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, - /* enable digital output on pin widget */ - { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, - {} /* terminator */ -}; - -#ifdef LIMITED_RATE_FMT_SUPPORT -/* support only the safe format and rate */ -#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 -#define SUPPORTED_MAXBPS 16 -#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE -#else -/* support all rates and formats */ -#define SUPPORTED_RATES \ - (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ - SNDRV_PCM_RATE_192000) -#define SUPPORTED_MAXBPS 24 -#define SUPPORTED_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) -#endif - -static int nvhdmi_7x_init_2ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); - return 0; -} - -static int nvhdmi_7x_init_8ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); - return 0; -} - -static const unsigned int channels_2_6_8[] = { - 2, 6, 8 -}; - -static const unsigned int channels_2_8[] = { - 2, 8 -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { - .count = ARRAY_SIZE(channels_2_6_8), - .list = channels_2_6_8, - .mask = 0, -}; - -static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { - .count = ARRAY_SIZE(channels_2_8), - .list = channels_2_8, - .mask = 0, -}; - -static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - const struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL; - - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - hw_constraints_channels = &hw_constraints_2_8_channels; - break; - case 0x10de0007: - hw_constraints_channels = &hw_constraints_2_6_8_channels; - break; - default: - break; - } - - if (hw_constraints_channels != NULL) { - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - hw_constraints_channels); - } else { - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); - } - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static const struct hda_pcm_stream simple_pcm_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .ops = { - .open = simple_playback_pcm_open, - .close = simple_playback_pcm_close, - .prepare = simple_playback_pcm_prepare - }, -}; - -static const struct hda_codec_ops simple_hdmi_patch_ops = { - .build_controls = simple_playback_build_controls, - .build_pcms = simple_playback_build_pcms, - .init = simple_playback_init, - .free = simple_playback_free, - .unsol_event = simple_hdmi_unsol_event, -}; - -static int patch_simple_hdmi(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t pin_nid) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - struct hdmi_spec_per_pin *per_pin; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - spec->codec = codec; - codec->spec = spec; - hdmi_array_init(spec, 1); - - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = cvt_nid; - spec->num_cvts = 1; - spec->num_pins = 1; - per_pin = snd_array_new(&spec->pins); - per_cvt = snd_array_new(&spec->cvts); - if (!per_pin || !per_cvt) { - simple_playback_free(codec); - return -ENOMEM; - } - per_cvt->cvt_nid = cvt_nid; - per_pin->pin_nid = pin_nid; - spec->pcm_playback = simple_pcm_playback; - - codec->patch_ops = simple_hdmi_patch_ops; - - return 0; -} - -static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, - int channels) -{ - unsigned int chanmask; - int chan = channels ? (channels - 1) : 1; - - switch (channels) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } - - /* Set the audio infoframe channel allocation and checksum fields. The - * channel count is computed implicitly by the hardware. */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); -} - -static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hdmi_spec *spec = codec->spec; - int i; - - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, - 0, AC_VERB_SET_CHANNEL_STREAMID, 0); - for (i = 0; i < 4; i++) { - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, - AC_VERB_SET_STREAM_FORMAT, 0); - } - - /* The audio hardware sends a channel count of 0x7 (8ch) when all the - * streams are disabled. */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int chs; - unsigned int dataDCC2, channel_id; - int i; - struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif; - struct hdmi_spec_per_cvt *per_cvt; - - mutex_lock(&codec->spdif_mutex); - per_cvt = get_cvt(spec, 0); - spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); - - chs = substream->runtime->channels; - - dataDCC2 = 0x2; - - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - - /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); - - /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, - AC_VERB_SET_STREAM_FORMAT, format); - - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_master_con_nid_7x, - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - - for (i = 0; i < 4; i++) { - if (chs == 2) - channel_id = 0; - else - channel_id = i * 2; - - /* turn off SPDIF once; - *otherwise the IEC958 bits won't be updated - */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & ~AC_DIG1_ENABLE & 0xff); - /* set the stream id */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_CHANNEL_STREAMID, - (stream_tag << 4) | channel_id); - /* set the stream format */ - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_STREAM_FORMAT, - format); - /* turn on again (if needed) */ - /* enable and set the channel status audio/data flag */ - if (codec->spdif_status_reset && - (spdif->ctls & AC_DIG1_ENABLE)) { - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_1, - spdif->ctls & 0xff); - snd_hda_codec_write(codec, - nvhdmi_con_nids_7x[i], - 0, - AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); - } - } - - nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); - - mutex_unlock(&codec->spdif_mutex); - return 0; -} - -static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - .nid = nvhdmi_master_con_nid_7x, - .rates = SUPPORTED_RATES, - .maxbps = SUPPORTED_MAXBPS, - .formats = SUPPORTED_FORMATS, - .ops = { - .open = simple_playback_pcm_open, - .close = nvhdmi_8ch_7x_pcm_close, - .prepare = nvhdmi_8ch_7x_pcm_prepare - }, -}; - -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x, - nvhdmi_master_pin_nid_7x); - if (err < 0) - return err; - - codec->patch_ops.init = nvhdmi_7x_init_2ch; - /* override the PCM rates, etc, as the codec doesn't give full list */ - spec = codec->spec; - spec->pcm_playback.rates = SUPPORTED_RATES; - spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; - spec->pcm_playback.formats = SUPPORTED_FORMATS; - spec->nv_dp_workaround = true; - return 0; -} - -static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int err = simple_playback_build_pcms(codec); - if (!err) { - struct hda_pcm *info = get_pcm_rec(spec, 0); - info->own_chmap = true; - } - return err; -} - -static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - struct hda_pcm *info; - struct snd_pcm_chmap *chmap; - int err; - - err = simple_playback_build_controls(codec); - if (err < 0) - return err; - - /* add channel maps */ - info = get_pcm_rec(spec, 0); - err = snd_pcm_add_chmap_ctls(info->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - snd_pcm_alt_chmaps, 8, 0, &chmap); - if (err < 0) - return err; - switch (codec->preset->vendor_id) { - case 0x10de0002: - case 0x10de0003: - case 0x10de0005: - case 0x10de0006: - chmap->channel_mask = (1U << 2) | (1U << 8); - break; - case 0x10de0007: - chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); - } - return 0; -} - -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_nvhdmi_2ch(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->multiout.max_channels = 8; - spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; - codec->patch_ops.init = nvhdmi_7x_init_8ch; - codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; - codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; - - /* Initialize the audio infoframe channel mask and checksum to something - * valid */ - nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); - - return 0; -} - -/* - * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: - * - 0x10de0015 - * - 0x10de0040 - */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, int channels) -{ - if (cap->ca_index == 0x00 && channels == 2) - return SNDRV_CTL_TLVT_CHMAP_FIXED; - - /* If the speaker allocation matches the channel count, it is OK. */ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) - return -EINVAL; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */ -static int nvhdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid - 4; -} - -/* reverse-map from port to pin NID: see above */ -static int nvhdmi_port2pin(struct hda_codec *codec, int port) -{ - return port + 4; -} - -static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { - .pin2port = nvhdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_nvhdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - codec->dp_mst = true; - - spec = codec->spec; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - generic_hdmi_init_per_pins(codec); - - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin); - - return 0; -} - -static int patch_nvhdmi_legacy(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = patch_generic_hdmi(codec); - if (err) - return err; - - spec = codec->spec; - spec->dyn_pin_out = true; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - codec->link_down_at_suspend = 1; - - return 0; -} - -/* - * The HDA codec on NVIDIA Tegra contains two scratch registers that are - * accessed using vendor-defined verbs. These registers can be used for - * interoperability between the HDA and HDMI drivers. - */ - -/* Audio Function Group node */ -#define NVIDIA_AFG_NID 0x01 - -/* - * The SCRATCH0 register is used to notify the HDMI codec of changes in audio - * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to - * be raised in the HDMI codec. The remainder of the bits is arbitrary. This - * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an - * additional bit (at position 30) to signal the validity of the format. - * - * | 31 | 30 | 29 16 | 15 0 | - * +---------+-------+--------+--------+ - * | TRIGGER | VALID | UNUSED | FORMAT | - * +-----------------------------------| - * - * Note that for the trigger bit to take effect it needs to change value - * (i.e. it needs to be toggled). The trigger bit is not applicable from - * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt - * trigger to hdmi. - */ -#define NVIDIA_SET_HOST_INTR 0xf80 -#define NVIDIA_GET_SCRATCH0 0xfa6 -#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 -#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 -#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 -#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa -#define NVIDIA_SCRATCH_TRIGGER (1 << 7) -#define NVIDIA_SCRATCH_VALID (1 << 6) - -#define NVIDIA_GET_SCRATCH1 0xfab -#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac -#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad -#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae -#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf - -/* - * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, - * the format is invalidated so that the HDMI codec can be disabled. - */ -static void tegra_hdmi_set_format(struct hda_codec *codec, - hda_nid_t cvt_nid, - unsigned int format) -{ - unsigned int value; - unsigned int nid = NVIDIA_AFG_NID; - struct hdmi_spec *spec = codec->spec; - - /* - * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. - * This resulted in moving scratch registers from audio function - * group to converter widget context. So CVT NID should be used for - * scratch register read/write for DP MST supported Tegra HDA codec. - */ - if (codec->dp_mst) - nid = cvt_nid; - - /* bits [31:30] contain the trigger and valid bits */ - value = snd_hda_codec_read(codec, nid, 0, - NVIDIA_GET_SCRATCH0, 0); - value = (value >> 24) & 0xff; - - /* bits [15:0] are used to store the HDA format */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE0, - (format >> 0) & 0xff); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE1, - (format >> 8) & 0xff); - - /* bits [16:24] are unused */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE2, 0); - - /* - * Bit 30 signals that the data is valid and hence that HDMI audio can - * be enabled. - */ - if (format == 0) - value &= ~NVIDIA_SCRATCH_VALID; - else - value |= NVIDIA_SCRATCH_VALID; - - if (spec->hdmi_intr_trig_ctrl) { - /* - * For Tegra HDA Codec design from TEGRA234 onwards, the - * Interrupt to hdmi driver is triggered by writing - * non-zero values to verb 0xF80 instead of 31st bit of - * scratch register. - */ - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_HOST_INTR, 0x1); - } else { - /* - * Whenever the 31st trigger bit is toggled, an interrupt is raised - * in the HDMI codec. The HDMI driver will use that as trigger - * to update its configuration. - */ - value ^= NVIDIA_SCRATCH_TRIGGER; - - snd_hda_codec_write(codec, nid, 0, - NVIDIA_SET_SCRATCH0_BYTE3, value); - } -} - -static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - int err; - - err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, - format, substream); - if (err < 0) - return err; - - /* notify the HDMI codec of the format change */ - tegra_hdmi_set_format(codec, hinfo->nid, format); - - return 0; -} - -static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - /* invalidate the format in the HDMI codec */ - tegra_hdmi_set_format(codec, hinfo->nid, 0); - - return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); -} - -static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) -{ - struct hdmi_spec *spec = codec->spec; - unsigned int i; - - for (i = 0; i < spec->num_pins; i++) { - struct hda_pcm *pcm = get_pcm_rec(spec, i); - - if (pcm->pcm_type == type) - return pcm; - } - - return NULL; -} - -static int tegra_hdmi_build_pcms(struct hda_codec *codec) -{ - struct hda_pcm_stream *stream; - struct hda_pcm *pcm; - int err; - - err = generic_hdmi_build_pcms(codec); - if (err < 0) - return err; - - pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); - if (!pcm) - return -ENODEV; - - /* - * Override ->prepare() and ->cleanup() operations to notify the HDMI - * codec about format changes. - */ - stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; - stream->ops.prepare = tegra_hdmi_pcm_prepare; - stream->ops.cleanup = tegra_hdmi_pcm_cleanup; - - return 0; -} - -static int tegra_hdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int i, err; - - err = hdmi_parse_codec(codec); - if (err < 0) { - generic_spec_free(codec); - return err; - } - - for (i = 0; i < spec->num_cvts; i++) - snd_hda_codec_write(codec, spec->cvt_nids[i], 0, - AC_VERB_SET_DIGI_CONVERT_1, - AC_DIG1_ENABLE); - - generic_hdmi_init_per_pins(codec); - - codec->depop_delay = 10; - codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - nvhdmi_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; - spec->nv_dp_workaround = true; - - return 0; -} - -static int patch_tegra_hdmi(struct hda_codec *codec) -{ - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - return tegra_hdmi_init(codec); -} - -static int patch_tegra234_hdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err; - - err = alloc_generic_hdmi(codec); - if (err < 0) - return err; - - codec->dp_mst = true; - spec = codec->spec; - spec->dyn_pin_out = true; - spec->hdmi_intr_trig_ctrl = true; - - return tegra_hdmi_init(codec); -} - -/* - * ATI/AMD-specific implementations - */ - -#define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->core.vendor_id == 0x1002aa01 && \ - ((codec)->core.revision_id & 0xff00) >= 0x0300) -#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) - -/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ -#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 -#define ATI_VERB_SET_DOWNMIX_INFO 0x772 -#define ATI_VERB_SET_MULTICHANNEL_01 0x777 -#define ATI_VERB_SET_MULTICHANNEL_23 0x778 -#define ATI_VERB_SET_MULTICHANNEL_45 0x779 -#define ATI_VERB_SET_MULTICHANNEL_67 0x77a -#define ATI_VERB_SET_HBR_CONTROL 0x77c -#define ATI_VERB_SET_MULTICHANNEL_1 0x785 -#define ATI_VERB_SET_MULTICHANNEL_3 0x786 -#define ATI_VERB_SET_MULTICHANNEL_5 0x787 -#define ATI_VERB_SET_MULTICHANNEL_7 0x788 -#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 -#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 -#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 -#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 -#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 -#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 -#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a -#define ATI_VERB_GET_HBR_CONTROL 0xf7c -#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 -#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 -#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 -#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 -#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 - -/* AMD specific HDA cvt verbs */ -#define ATI_VERB_SET_RAMP_RATE 0x770 -#define ATI_VERB_GET_RAMP_RATE 0xf70 - -#define ATI_OUT_ENABLE 0x1 - -#define ATI_MULTICHANNEL_MODE_PAIRED 0 -#define ATI_MULTICHANNEL_MODE_SINGLE 1 - -#define ATI_HBR_CAPABLE 0x01 -#define ATI_HBR_ENABLE 0x10 - -static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, - int dev_id, unsigned char *buf, int *eld_size) -{ - WARN_ON(dev_id != 0); - /* call hda_eld.c ATI/AMD-specific function */ - return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size, - is_amdhdmi_rev3_or_later(codec)); -} - -static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, int dev_id, int ca, - int active_channels, int conn_type) -{ - WARN_ON(dev_id != 0); - snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); -} - -static int atihdmi_paired_swap_fc_lfe(int pos) -{ - /* - * ATI/AMD have automatic FC/LFE swap built-in - * when in pairwise mapping mode. - */ - - switch (pos) { - /* see channel_allocations[].speakers[] */ - case 2: return 3; - case 3: return 2; - default: break; - } - - return pos; -} - -static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, - int ca, int chs, unsigned char *map) -{ - struct hdac_cea_channel_speaker_allocation *cap; - int i, j; - - /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - - cap = snd_hdac_get_ch_alloc_from_ca(ca); - for (i = 0; i < chs; ++i) { - int mask = snd_hdac_chmap_to_spk_mask(map[i]); - bool ok = false; - bool companion_ok = false; - - if (!mask) - continue; - - for (j = 0 + i % 2; j < 8; j += 2) { - int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); - if (cap->speakers[chan_idx] == mask) { - /* channel is in a supported position */ - ok = true; - - if (i % 2 == 0 && i + 1 < chs) { - /* even channel, check the odd companion */ - int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); - int comp_mask_act = cap->speakers[comp_chan_idx]; - - if (comp_mask_req == comp_mask_act) - companion_ok = true; - else - return -EINVAL; - } - break; - } - } - - if (!ok) - return -EINVAL; - - if (companion_ok) - i++; /* companion channel already checked */ - } - - return 0; -} - -static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int hdmi_slot, int stream_channel) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - int verb; - int ati_channel_setup = 0; - - if (hdmi_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); - - /* In case this is an odd slot but without stream channel, do not - * disable the slot since the corresponding even slot could have a - * channel. In case neither have a channel, the slot pair will be - * disabled when this function is called for the even slot. */ - if (hdmi_slot % 2 != 0 && stream_channel == 0xf) - return 0; - - hdmi_slot -= hdmi_slot % 2; - - if (stream_channel != 0xf) - stream_channel -= stream_channel % 2; - } - - verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; - - /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ - - if (stream_channel != 0xf) - ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; - - return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); -} - -static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, - hda_nid_t pin_nid, int asp_slot) -{ - struct hda_codec *codec = hdac_to_hda_codec(hdac); - bool was_odd = false; - int ati_asp_slot = asp_slot; - int verb; - int ati_channel_setup; - - if (asp_slot > 7) - return -EINVAL; - - if (!has_amd_full_remap_support(codec)) { - ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); - if (ati_asp_slot % 2 != 0) { - ati_asp_slot -= 1; - was_odd = true; - } - } - - verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; - - ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); - - if (!(ati_channel_setup & ATI_OUT_ENABLE)) - return 0xf; - - return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; -} - -static int atihdmi_paired_chmap_cea_alloc_validate_get_type( - struct hdac_chmap *chmap, - struct hdac_cea_channel_speaker_allocation *cap, - int channels) -{ - int c; - - /* - * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so - * we need to take that into account (a single channel may take 2 - * channel slots if we need to carry a silent channel next to it). - * On Rev3+ AMD codecs this function is not used. - */ - int chanpairs = 0; - - /* We only produce even-numbered channel count TLVs */ - if ((channels % 2) != 0) - return -1; - - for (c = 0; c < 7; c += 2) { - if (cap->speakers[c] || cap->speakers[c+1]) - chanpairs++; - } - - if (chanpairs * 2 != channels) - return -1; - - return SNDRV_CTL_TLVT_CHMAP_PAIRED; -} - -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, - struct hdac_cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - /* produce paired maps for pre-rev3 ATI/AMD codecs */ - int count = 0; - int c; - - for (c = 7; c >= 0; c--) { - int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); - int spk = cap->speakers[chan]; - if (!spk) { - /* add N/A channel if the companion channel is occupied */ - if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) - chmap[count++] = SNDRV_CHMAP_NA; - - continue; - } - - chmap[count++] = snd_hdac_spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - -static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, - int dev_id, bool hbr) -{ - int hbr_ctl, hbr_ctl_new; - - WARN_ON(dev_id != 0); - - hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); - if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) { - if (hbr) - hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; - else - hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; - - codec_dbg(codec, - "atihdmi_pin_hbr_setup: NID=0x%x, %shbr-ctl=0x%x\n", - pin_nid, - hbr_ctl == hbr_ctl_new ? "" : "new-", - hbr_ctl_new); - - if (hbr_ctl != hbr_ctl_new) - snd_hda_codec_write(codec, pin_nid, 0, - ATI_VERB_SET_HBR_CONTROL, - hbr_ctl_new); - - } else if (hbr) - return -EINVAL; - - return 0; -} - -static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, int dev_id, - u32 stream_tag, int format) -{ - if (is_amdhdmi_rev3_or_later(codec)) { - int ramp_rate = 180; /* default as per AMD spec */ - /* disable ramp-up/down for non-pcm as per AMD spec */ - if (format & AC_FMT_TYPE_NON_PCM) - ramp_rate = 0; - - snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); - } - - return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); -} - - -static int atihdmi_init(struct hda_codec *codec) -{ - struct hdmi_spec *spec = codec->spec; - int pin_idx, err; - - err = generic_hdmi_init(codec); - - if (err) - return err; - - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - - /* make sure downmix information in infoframe is zero */ - snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); - - /* enable channel-wise remap mode if supported */ - if (has_amd_full_remap_support(codec)) - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - ATI_VERB_SET_MULTICHANNEL_MODE, - ATI_MULTICHANNEL_MODE_SINGLE); - } - codec->auto_runtime_pm = 1; - - return 0; -} - -/* map from pin NID to port; port is 0-based */ -/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ -static int atihdmi_pin2port(void *audio_ptr, int pin_nid) -{ - return pin_nid / 2 - 1; -} - -/* reverse-map from port to pin NID: see above */ -static int atihdmi_port2pin(struct hda_codec *codec, int port) -{ - return port * 2 + 3; -} - -static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { - .pin2port = atihdmi_pin2port, - .pin_eld_notify = generic_acomp_pin_eld_notify, - .master_bind = generic_acomp_master_bind, - .master_unbind = generic_acomp_master_unbind, -}; - -static int patch_atihdmi(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - struct hdmi_spec_per_cvt *per_cvt; - int err, cvt_idx; - - err = patch_generic_hdmi(codec); - - if (err) - return err; - - codec->patch_ops.init = atihdmi_init; - - spec = codec->spec; - - spec->static_pcm_mapping = true; - - spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; - spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; - spec->ops.setup_stream = atihdmi_setup_stream; - - spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; - - if (!has_amd_full_remap_support(codec)) { - /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->chmap.ops.chmap_cea_alloc_validate_get_type = - atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->chmap.ops.cea_alloc_to_tlv_chmap = - atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; - } - - /* ATI/AMD converters do not advertise all of their capabilities */ - for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { - per_cvt = get_cvt(spec, cvt_idx); - per_cvt->channels_max = max(per_cvt->channels_max, 8u); - per_cvt->rates |= SUPPORTED_RATES; - per_cvt->formats |= SUPPORTED_FORMATS; - per_cvt->maxbps = max(per_cvt->maxbps, 24u); - } - - spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); - - /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing - * the link-down as is. Tell the core to allow it. - */ - codec->link_down_at_suspend = 1; - - generic_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin); - - return 0; -} - -/* VIA HDMI Implementation */ -#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ -#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ - -static int patch_via_hdmi(struct hda_codec *codec) -{ - return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); -} static int patch_gf_hdmi(struct hda_codec *codec) { @@ -4509,125 +2367,15 @@ static int patch_gf_hdmi(struct hda_codec *codec) */ static const struct hda_device_id snd_hda_id_hdmi[] = { HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy), -/* 17 is known to be absent */ -HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0033, "SoC 33 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0034, "Tegra264 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0035, "SoC 35 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009b, "GPU 9b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009c, "GPU 9c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a1, "GPU a1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a8, "GPU a8 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a9, "GPU a9 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00aa, "GPU aa HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ab, "GPU ab HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ad, "GPU ad HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ae, "GPU ae HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00af, "GPU af HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00b0, "GPU b0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00b1, "GPU b1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c0, "GPU c0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c1, "GPU c1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c3, "GPU c3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c4, "GPU c4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c5, "GPU c5 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x1d179f86, "ZX-100S HDMI/DP", patch_gf_hdmi), @@ -4641,40 +2389,10 @@ HDA_CODEC_ENTRY(0x1d179f8d, "KX-6000G HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f8e, "KX-7000 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f8f, "KX-7000 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f90, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862823, "Wildcat Lake HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), /* special ID for generic HDMI */ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), @@ -4684,9 +2402,6 @@ MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("HDMI HD-audio codec"); -MODULE_ALIAS("snd-hda-codec-intelhdmi"); -MODULE_ALIAS("snd-hda-codec-nvhdmi"); -MODULE_ALIAS("snd-hda-codec-atihdmi"); static struct hda_codec_driver hdmi_driver = { .id = snd_hda_id_hdmi, diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h new file mode 100644 index 0000000000000..96351bebbc1b4 --- /dev/null +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HD-audio HDMI codec driver + */ + +#ifndef __HDA_HDMI_LOCAL_H +#define __HDA_HDMI_LOCAL_H + +#include +#include +#include +#include +#include +#include +#include "hda_local.h" + +struct hdmi_spec_per_cvt { + hda_nid_t cvt_nid; + bool assigned; /* the stream has been assigned */ + bool silent_stream; /* silent stream activated */ + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + +struct hdmi_spec_per_pin { + hda_nid_t pin_nid; + int dev_id; + /* pin idx, different device entries on the same pin use the same idx */ + int pin_nid_idx; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + int mux_idx; + hda_nid_t cvt_nid; + + struct hda_codec *codec; + struct hdmi_eld sink_eld; + struct mutex lock; + struct delayed_work work; + struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ + int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ + int prev_pcm_idx; /* previously assigned pcm index */ + int repoll_count; + bool setup; /* the stream has been set up by prepare callback */ + bool silent_stream; + int channels; /* current number of channels */ + bool non_pcm; + bool chmap_set; /* channel-map override by ALSA API? */ + unsigned char chmap[8]; /* ALSA API channel-map */ +#ifdef CONFIG_SND_PROC_FS + struct snd_info_entry *proc_entry; +#endif +}; + +/* operations used by generic code that can be overridden by patches */ +struct hdmi_ops { + int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, unsigned char *buf, int *eld_size); + + void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, + int ca, int active_channels, int conn_type); + + /* enable/disable HBR (HD passthrough) */ + int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, + int dev_id, bool hbr); + + int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, u32 stream_tag, + int format); + + void (*pin_cvt_fixup)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid); + + void (*silent_stream)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable); +}; + +struct hdmi_pcm { + struct hda_pcm *pcm; + struct snd_jack *jack; + struct snd_kcontrol *eld_ctl; +}; + +enum { + SILENT_STREAM_OFF = 0, + SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */ + SILENT_STREAM_I915, /* Intel i915 extension */ +}; + +struct hdmi_spec { + struct hda_codec *codec; + int num_cvts; + struct snd_array cvts; /* struct hdmi_spec_per_cvt */ + hda_nid_t cvt_nids[4]; /* only for haswell fix */ + + /* + * num_pins is the number of virtual pins + * for example, there are 3 pins, and each pin + * has 4 device entries, then the num_pins is 12 + */ + int num_pins; + /* + * num_nids is the number of real pins + * In the above example, num_nids is 3 + */ + int num_nids; + /* + * dev_num is the number of device entries + * on each pin. + * In the above example, dev_num is 4 + */ + int dev_num; + struct snd_array pins; /* struct hdmi_spec_per_pin */ + struct hdmi_pcm pcm_rec[8]; + struct mutex pcm_lock; + struct mutex bind_lock; /* for audio component binding */ + /* pcm_bitmap means which pcms have been assigned to pins*/ + unsigned long pcm_bitmap; + int pcm_used; /* counter of pcm_rec[] */ + /* bitmap shows whether the pcm is opened in user space + * bit 0 means the first playback PCM (PCM3); + * bit 1 means the second playback PCM, and so on. + */ + unsigned long pcm_in_use; + + struct hdmi_eld temp_eld; + struct hdmi_ops ops; + + bool dyn_pin_out; + bool static_pcm_mapping; + /* hdmi interrupt trigger control flag for Nvidia codec */ + bool hdmi_intr_trig_ctrl; + bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */ + + bool intel_hsw_fixup; /* apply Intel platform-specific fixups */ + /* + * Non-generic VIA/NVIDIA specific + */ + struct hda_multi_out multiout; + struct hda_pcm_stream pcm_playback; + + bool use_acomp_notifier; /* use eld_notify callback for hotplug */ + bool acomp_registered; /* audio component registered in this driver */ + bool force_connect; /* force connectivity */ + struct drm_audio_component_audio_ops drm_audio_ops; + int (*port2pin)(struct hda_codec *codec, int port); /* reverse port/pin mapping */ + + struct hdac_chmap chmap; + hda_nid_t vendor_nid; + const int *port_map; + int port_num; + int silent_stream_type; + + const struct snd_pcm_hw_constraint_list *hw_constraints_channels; +}; + +#ifdef CONFIG_SND_HDA_COMPONENT +static inline bool codec_has_acomp(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + return spec->use_acomp_notifier; +} +#else +#define codec_has_acomp(codec) false +#endif + +struct hdmi_audio_infoframe { + u8 type; /* 0x84 */ + u8 ver; /* 0x01 */ + u8 len; /* 0x0a */ + + u8 checksum; + + u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +union audio_infoframe { + struct hdmi_audio_infoframe hdmi; + struct dp_audio_infoframe dp; + DECLARE_FLEX_ARRAY(u8, bytes); +}; + +#ifdef LIMITED_RATE_FMT_SUPPORT +/* support only the safe format and rate */ +#define SUPPORTED_RATES SNDRV_PCM_RATE_48000 +#define SUPPORTED_MAXBPS 16 +#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE +#else +/* support all rates and formats */ +#define SUPPORTED_RATES \ + (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define SUPPORTED_MAXBPS 24 +#define SUPPORTED_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#endif + +/* + * HDMI routines + */ + +#define get_pin(spec, idx) \ + ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) +#define get_cvt(spec, idx) \ + ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) +/* obtain hdmi_pcm object assigned to idx */ +#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) +/* obtain hda_pcm object assigned to idx */ +#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) + +/* Generic HDMI codec support */ +int snd_hda_hdmi_generic_alloc(struct hda_codec *codec); +int snd_hda_hdmi_parse_codec(struct hda_codec *codec); +int patch_generic_hdmi(struct hda_codec *codec); +void snd_hda_hdmi_generic_free(struct hda_codec *codec); + +int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec); +int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec); +int snd_hda_hdmi_generic_init(struct hda_codec *codec); +int snd_hda_hdmi_generic_suspend(struct hda_codec *codec); +int snd_hda_hdmi_generic_resume(struct hda_codec *codec); +void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res); + +int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id); +#define pin_id_to_pin_index(codec, pin, dev) \ + snd_hda_hdmi_pin_id_to_pin_index(codec, pin, dev) +int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec); +void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec); +int snd_hda_hdmi_setup_stream(struct hda_codec *codec, + hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, + u32 stream_tag, int format); + +int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream); +int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream); + +void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec, + hda_nid_t nid, int dev_id); +void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm); + +/* Audio component support */ +void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops); +void snd_hda_hdmi_acomp_init(struct hda_codec *codec, + const struct drm_audio_component_audio_ops *ops, + int (*port2pin)(struct hda_codec *, int)); +void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id); +int snd_hda_hdmi_acomp_master_bind(struct device *dev, + struct drm_audio_component *acomp); +void snd_hda_hdmi_acomp_master_unbind(struct device *dev, + struct drm_audio_component *acomp); + +/* Simple / legacy HDMI codec support */ +int patch_simple_hdmi(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid); +void snd_hda_hdmi_simple_free(struct hda_codec *codec); + +int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec); +int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec); +int snd_hda_hdmi_simple_init(struct hda_codec *codec); +void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec, + unsigned int res); +int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream); + +#endif /* __HDA_HDMI_LOCAL_H */ diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c new file mode 100644 index 0000000000000..a88ac1f80db6a --- /dev/null +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -0,0 +1,760 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Intel HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +static bool enable_silent_stream = +IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); +module_param(enable_silent_stream, bool, 0644); +MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); + +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void intel_haswell_enable_all_pins(struct hda_codec *codec, + bool update_tree) +{ + unsigned int vendor_param; + struct hdmi_spec *spec = codec->spec; + + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + + if (update_tree) + snd_hda_codec_update_widgets(codec); +} + +static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) +{ + unsigned int vendor_param; + struct hdmi_spec *spec = codec->spec; + + vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); + snd_hda_codec_write_cache(codec, spec->vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); +} + +/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. + * Otherwise you may get severe h/w communication errors. + */ +static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + if (power_state == AC_PWRST_D0) { + intel_haswell_enable_all_pins(codec, false); + intel_haswell_fixup_enable_dp12(codec); + } + + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state); +} + +/* There is a fixed mapping between audio pin node and display port. + * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: + * Pin Widget 5 - PORT B (port = 1 in i915 driver) + * Pin Widget 6 - PORT C (port = 2 in i915 driver) + * Pin Widget 7 - PORT D (port = 3 in i915 driver) + * + * on VLV, ILK: + * Pin Widget 4 - PORT B (port = 1 in i915 driver) + * Pin Widget 5 - PORT C (port = 2 in i915 driver) + * Pin Widget 6 - PORT D (port = 3 in i915 driver) + */ +static int intel_base_nid(struct hda_codec *codec) +{ + switch (codec->core.vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + return 4; + default: + return 5; + } +} + +static int intel_pin2port(void *audio_ptr, int pin_nid) +{ + struct hda_codec *codec = audio_ptr; + struct hdmi_spec *spec = codec->spec; + int base_nid, i; + + if (!spec->port_num) { + base_nid = intel_base_nid(codec); + if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) + return -1; + return pin_nid - base_nid + 1; + } + + /* + * looking for the pin number in the mapping table and return + * the index which indicate the port number + */ + for (i = 0; i < spec->port_num; i++) { + if (pin_nid == spec->port_map[i]) + return i; + } + + codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); + return -1; +} + +static int intel_port2pin(struct hda_codec *codec, int port) +{ + struct hdmi_spec *spec = codec->spec; + + if (!spec->port_num) { + /* we assume only from port-B to port-D */ + if (port < 1 || port > 3) + return 0; + return port + intel_base_nid(codec) - 1; + } + + if (port < 0 || port >= spec->port_num) + return 0; + return spec->port_map[port]; +} + +static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) +{ + struct hda_codec *codec = audio_ptr; + int pin_nid; + int dev_id = pipe; + + pin_nid = intel_port2pin(codec, port); + if (!pin_nid) + return; + /* skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume + */ + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + return; + + snd_hdac_i915_set_bclk(&codec->bus->core); + snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); +} + +static const struct drm_audio_component_audio_ops intel_audio_ops = { + .pin2port = intel_pin2port, + .pin_eld_notify = intel_pin_eld_notify, +}; + +/* register i915 component pin_eld_notify callback */ +static void register_i915_notifier(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + spec->use_acomp_notifier = true; + spec->port2pin = intel_port2pin; + snd_hda_hdmi_setup_drm_audio_ops(codec, &intel_audio_ops); + snd_hdac_acomp_register_notifier(&codec->bus->core, + &spec->drm_audio_ops); + /* no need for forcible resume for jack check thanks to notifier */ + codec->relaxed_resume = 1; +} + +#define I915_SILENT_RATE 48000 +#define I915_SILENT_CHANNELS 2 +#define I915_SILENT_FORMAT_BITS 16 +#define I915_SILENT_FMT_MASK 0xf + +static void silent_stream_enable_i915(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + unsigned int format; + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS, + I915_SILENT_RATE); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; + snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); +} + +static void silent_stream_set_kae(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + unsigned int param; + + codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid); + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + param = (param >> 16) & 0xff; + + if (enable) + param |= AC_DIG3_KAE; + else + param &= ~AC_DIG3_KAE; + + snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param); +} + +static void i915_set_silent_stream(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool enable) +{ + struct hdmi_spec *spec = codec->spec; + + switch (spec->silent_stream_type) { + case SILENT_STREAM_KAE: + if (enable) { + silent_stream_enable_i915(codec, per_pin); + silent_stream_set_kae(codec, per_pin, true); + } else { + silent_stream_set_kae(codec, per_pin, false); + } + break; + case SILENT_STREAM_I915: + if (enable) { + silent_stream_enable_i915(codec, per_pin); + snd_hda_power_up_pm(codec); + } else { + /* release ref taken in silent_stream_enable() */ + snd_hda_power_down_pm(codec); + } + break; + default: + break; + } +} + +static void haswell_verify_D0(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t nid) +{ + int pwr; + + /* For Haswell, the converter 1/2 may keep in D3 state after bootup, + * thus pins could only choose converter 0 for use. Make sure the + * converters are in correct power state + */ + if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0)) + snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) { + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + msleep(40); + pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; + codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); + } +} + +/* Assure the pin select the right convetor */ +static void intel_verify_pin_cvt_connect(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + hda_nid_t pin_nid = per_pin->pin_nid; + int mux_idx, curr; + + mux_idx = per_pin->mux_idx; + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (curr != mux_idx) + snd_hda_codec_write_cache(codec, pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); +} + +/* get the mux index for the converter of the pins + * converter's mux index is the same for all pins on Intel platform + */ +static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, + hda_nid_t cvt_nid) +{ + int i; + + for (i = 0; i < spec->num_cvts; i++) + if (spec->cvt_nids[i] == cvt_nid) + return i; + return -EINVAL; +} + +/* Intel HDMI workaround to fix audio routing issue: + * For some Intel display codecs, pins share the same connection list. + * So a conveter can be selected by multiple pins and playback on any of these + * pins will generate sound on the external display, because audio flows from + * the same converter to the display pipeline. Also muting one pin may make + * other pins have no sound output. + * So this function assures that an assigned converter for a pin is not selected + * by any other pins. + */ +static void intel_not_share_assigned_cvt(struct hda_codec *codec, + hda_nid_t pin_nid, + int dev_id, int mux_idx) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t nid; + int cvt_idx, curr; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + int pin_idx; + + /* configure the pins connections */ + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + int dev_id_saved; + int dev_num; + + per_pin = get_pin(spec, pin_idx); + /* + * pin not connected to monitor + * no need to operate on it + */ + if (!per_pin->pcm) + continue; + + if ((per_pin->pin_nid == pin_nid) && + (per_pin->dev_id == dev_id)) + continue; + + /* + * if per_pin->dev_id >= dev_num, + * snd_hda_get_dev_select() will fail, + * and the following operation is unpredictable. + * So skip this situation. + */ + dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1; + if (per_pin->dev_id >= dev_num) + continue; + + nid = per_pin->pin_nid; + + /* + * Calling this function should not impact + * on the device entry selection + * So let's save the dev id for each pin, + * and restore it when return + */ + dev_id_saved = snd_hda_get_dev_select(codec, nid); + snd_hda_set_dev_select(codec, nid, per_pin->dev_id); + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (curr != mux_idx) { + snd_hda_set_dev_select(codec, nid, dev_id_saved); + continue; + } + + + /* choose an unassigned converter. The conveters in the + * connection list are in the same order as in the codec. + */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + if (!per_cvt->assigned) { + codec_dbg(codec, + "choose cvt %d for pin NID 0x%x\n", + cvt_idx, nid); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + cvt_idx); + break; + } + } + snd_hda_set_dev_select(codec, nid, dev_id_saved); + } +} + +/* A wrapper of intel_not_share_asigned_cvt() */ +static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, + hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) +{ + int mux_idx; + struct hdmi_spec *spec = codec->spec; + + /* On Intel platform, the mapping of converter nid to + * mux index of the pins are always the same. + * The pin nid may be 0, this means all pins will not + * share the converter. + */ + mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); + if (mux_idx >= 0) + intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx); +} + +/* setup_stream ops override for HSW+ */ +static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, u32 stream_tag, + int format) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); + struct hdmi_spec_per_pin *per_pin; + int res; + + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); + + haswell_verify_D0(codec, cvt_nid, pin_nid); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } + + res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + usleep_range(100, 200); + silent_stream_set_kae(codec, per_pin, true); + } + + return res; +} + +/* pin_cvt_fixup ops override for HSW+ and VLV+ */ +static void i915_pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + if (per_pin) { + haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid); + snd_hda_set_dev_select(codec, per_pin->pin_nid, + per_pin->dev_id); + intel_verify_pin_cvt_connect(codec, per_pin); + intel_not_share_assigned_cvt(codec, per_pin->pin_nid, + per_pin->dev_id, per_pin->mux_idx); + } else { + intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid); + } +} + +static int i915_adlp_hdmi_suspend(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + bool silent_streams = false; + int pin_idx, res; + + res = snd_hda_hdmi_generic_suspend(codec); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + if (per_pin->silent_stream) { + silent_streams = true; + break; + } + } + + if (silent_streams) { + /* + * stream-id should remain programmed when codec goes + * to runtime suspend + */ + codec->no_stream_clean_at_suspend = 1; + + /* + * the system might go to S3, in which case keep-alive + * must be reprogrammed upon resume + */ + codec->forced_resume = 1; + + codec_dbg(codec, "HDMI: KAE active at suspend\n"); + } else { + codec->no_stream_clean_at_suspend = 0; + codec->forced_resume = 0; + } + + return res; +} + +static int i915_adlp_hdmi_resume(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, res; + + res = snd_hda_hdmi_generic_resume(codec); + + /* KAE not programmed at suspend, nothing to do here */ + if (!codec->no_stream_clean_at_suspend) + return res; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* + * If system was in suspend with monitor connected, + * the codec setting may have been lost. Re-enable + * keep-alive. + */ + if (per_pin->silent_stream) { + unsigned int param; + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_CONV, 0); + if (!param) { + codec_dbg(codec, "HDMI: KAE: restore stream id\n"); + silent_stream_enable_i915(codec, per_pin); + } + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + if (!(param & (AC_DIG3_KAE << 16))) { + codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); + silent_stream_set_kae(codec, per_pin, true); + } + } + } + + return res; +} + +/* precondition and allocation for Intel codecs */ +static int alloc_intel_hdmi(struct hda_codec *codec) +{ + int err; + + /* requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + /* set probe_id here to prevent generic fallback binding */ + codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; + return -ENODEV; + } + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + /* no need to handle unsol events */ + codec->patch_ops.unsol_event = NULL; + return 0; +} + +/* parse and post-process for Intel codecs */ +static int parse_intel_hdmi(struct hda_codec *codec) +{ + int err, retries = 3; + + do { + err = snd_hda_hdmi_parse_codec(codec); + } while (err < 0 && retries--); + + if (err < 0) + return err; + + snd_hda_hdmi_generic_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel Haswell and onwards; audio component with eld notifier */ +static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, + const int *port_map, int port_num, int dev_num, + bool send_silent_stream) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_intel_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; + codec->dp_mst = true; + spec->vendor_nid = vendor_nid; + spec->port_map = port_map; + spec->port_num = port_num; + spec->intel_hsw_fixup = true; + spec->dev_num = dev_num; + + intel_haswell_enable_all_pins(codec, true); + intel_haswell_fixup_enable_dp12(codec); + + codec->display_power_control = 1; + + codec->patch_ops.set_power_state = haswell_set_power_state; + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + spec->ops.setup_stream = i915_hsw_setup_stream; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + spec->ops.silent_stream = i915_set_silent_stream; + + /* + * Enable silent stream feature, if it is enabled via + * module param or Kconfig option + */ + if (send_silent_stream) + spec->silent_stream_type = SILENT_STREAM_I915; + + return parse_intel_hdmi(codec); +} + +static int patch_i915_hsw_hdmi(struct hda_codec *codec) +{ + return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, + enable_silent_stream); +} + +static int patch_i915_glk_hdmi(struct hda_codec *codec) +{ + /* + * Silent stream calls audio component .get_power() from + * .pin_eld_notify(). On GLK this will deadlock in i915 due + * to the audio vs. CDCLK workaround. + */ + return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); +} + +static int patch_i915_icl_hdmi(struct hda_codec *codec) +{ + /* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number. + */ + static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb}; + + return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3, + enable_silent_stream); +} + +static int patch_i915_tgl_hdmi(struct hda_codec *codec) +{ + /* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number. + */ + static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + + return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4, + enable_silent_stream); +} + +static int patch_i915_adlp_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int res; + + res = patch_i915_tgl_hdmi(codec); + if (!res) { + spec = codec->spec; + + if (spec->silent_stream_type) { + spec->silent_stream_type = SILENT_STREAM_KAE; + + codec->patch_ops.resume = i915_adlp_hdmi_resume; + codec->patch_ops.suspend = i915_adlp_hdmi_suspend; + } + } + + return res; +} + +/* Intel Baytrail and Braswell; with eld notifier */ +static int patch_i915_byt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = alloc_intel_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; + + /* For Valleyview/Cherryview, only the display codec is in the display + * power well and can use link_power ops to request/release the power. + */ + codec->display_power_control = 1; + + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + + return parse_intel_hdmi(codec); +} + +/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ +static int patch_i915_cpt_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_intel_hdmi(codec); + if (err < 0) + return err; + return parse_intel_hdmi(codec); +} + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_intelhdmi[] = { +HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), +HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), +HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), +HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), +HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), +HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), +HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862823, "Wildcat Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), +HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), +{} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_intelhdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver intelhdmi_driver = { + .id = snd_hda_id_intelhdmi, +}; + +module_hda_codec_driver(intelhdmi_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c new file mode 100644 index 0000000000000..67e187a351d7b --- /dev/null +++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Legacy Nvidia HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +#define Nv_VERB_SET_Channel_Allocation 0xF79 +#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A +#define Nv_VERB_SET_Audio_Protection_On 0xF98 +#define Nv_VERB_SET_Audio_Protection_Off 0xF99 + +#define nvhdmi_master_con_nid_7x 0x04 +#define nvhdmi_master_pin_nid_7x 0x05 + +static const hda_nid_t nvhdmi_con_nids_7x[4] = { + /*front, rear, clfe, rear_surr */ + 0x6, 0x8, 0xa, 0xc, +}; + +static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, + /* enable digital output on pin widget */ + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + {} /* terminator */ +}; + +static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { + /* set audio protect on */ + { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, + /* enable digital output on pin widget */ + { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 }, + {} /* terminator */ +}; + +static int nvhdmi_7x_init_2ch(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); + return 0; +} + +static int nvhdmi_7x_init_8ch(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); + return 0; +} + +static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, + int channels) +{ + unsigned int chanmask; + int chan = channels ? (channels - 1) : 1; + + switch (channels) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + + /* Set the audio infoframe channel allocation and checksum fields. The + * channel count is computed implicitly by the hardware. + */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); +} + +static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + int i; + + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, + 0, AC_VERB_SET_CHANNEL_STREAMID, 0); + for (i = 0; i < 4; i++) { + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, + AC_VERB_SET_STREAM_FORMAT, 0); + } + + /* The audio hardware sends a channel count of 0x7 (8ch) when all the + * streams are disabled. + */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int chs; + unsigned int dataDCC2, channel_id; + int i; + struct hdmi_spec *spec = codec->spec; + struct hda_spdif_out *spdif; + struct hdmi_spec_per_cvt *per_cvt; + + mutex_lock(&codec->spdif_mutex); + per_cvt = get_cvt(spec, 0); + spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid); + + chs = substream->runtime->channels; + + dataDCC2 = 0x2; + + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); + + /* set the stream id */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); + + /* set the stream format */ + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, + AC_VERB_SET_STREAM_FORMAT, format); + + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_master_con_nid_7x, + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + + for (i = 0; i < 4; i++) { + if (chs == 2) + channel_id = 0; + else + channel_id = i * 2; + + /* turn off SPDIF once; + *otherwise the IEC958 bits won't be updated + */ + if (codec->spdif_status_reset && + (spdif->ctls & AC_DIG1_ENABLE)) + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & ~AC_DIG1_ENABLE & 0xff); + /* set the stream id */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_CHANNEL_STREAMID, + (stream_tag << 4) | channel_id); + /* set the stream format */ + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_STREAM_FORMAT, + format); + /* turn on again (if needed) */ + /* enable and set the channel status audio/data flag */ + if (codec->spdif_status_reset && + (spdif->ctls & AC_DIG1_ENABLE)) { + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_1, + spdif->ctls & 0xff); + snd_hda_codec_write(codec, + nvhdmi_con_nids_7x[i], + 0, + AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); + } + } + + nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); + + mutex_unlock(&codec->spdif_mutex); + return 0; +} + +static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = nvhdmi_master_con_nid_7x, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, + .ops = { + .open = snd_hda_hdmi_simple_pcm_open, + .close = nvhdmi_8ch_7x_pcm_close, + .prepare = nvhdmi_8ch_7x_pcm_prepare + }, +}; + +static int patch_nvhdmi_2ch(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x, + nvhdmi_master_pin_nid_7x); + if (err < 0) + return err; + + codec->patch_ops.init = nvhdmi_7x_init_2ch; + /* override the PCM rates, etc, as the codec doesn't give full list */ + spec = codec->spec; + spec->pcm_playback.rates = SUPPORTED_RATES; + spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; + spec->pcm_playback.formats = SUPPORTED_FORMATS; + spec->nv_dp_workaround = true; + return 0; +} + +static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int err; + + err = snd_hda_hdmi_simple_build_pcms(codec); + if (!err) { + struct hda_pcm *info = get_pcm_rec(spec, 0); + + info->own_chmap = true; + } + return err; +} + +static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info; + struct snd_pcm_chmap *chmap; + int err; + + err = snd_hda_hdmi_simple_build_controls(codec); + if (err < 0) + return err; + + /* add channel maps */ + info = get_pcm_rec(spec, 0); + err = snd_pcm_add_chmap_ctls(info->pcm, + SNDRV_PCM_STREAM_PLAYBACK, + snd_pcm_alt_chmaps, 8, 0, &chmap); + if (err < 0) + return err; + switch (codec->preset->vendor_id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + chmap->channel_mask = (1U << 2) | (1U << 8); + break; + case 0x10de0007: + chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8); + } + return 0; +} + +static const unsigned int channels_2_6_8[] = { + 2, 6, 8 +}; + +static const unsigned int channels_2_8[] = { + 2, 8 +}; + +static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = { + .count = ARRAY_SIZE(channels_2_6_8), + .list = channels_2_6_8, + .mask = 0, +}; + +static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { + .count = ARRAY_SIZE(channels_2_8), + .list = channels_2_8, + .mask = 0, +}; + +static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = patch_nvhdmi_2ch(codec); + if (err < 0) + return err; + spec = codec->spec; + spec->multiout.max_channels = 8; + spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; + codec->patch_ops.init = nvhdmi_7x_init_8ch; + codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; + codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; + + switch (codec->preset->vendor_id) { + case 0x10de0002: + case 0x10de0003: + case 0x10de0005: + case 0x10de0006: + spec->hw_constraints_channels = &hw_constraints_2_8_channels; + break; + case 0x10de0007: + spec->hw_constraints_channels = &hw_constraints_2_6_8_channels; + break; + default: + break; + } + + /* Initialize the audio infoframe channel mask and checksum to something + * valid + */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + + return 0; +} + +/* + * patch entries + */ +static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = { +HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), +HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), +HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), +{} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver nvhdmi_mcp_driver = { + .id = snd_hda_id_nvhdmi_mcp, +}; + +module_hda_codec_driver(nvhdmi_mcp_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi.c b/sound/hda/codecs/hdmi/nvhdmi.c new file mode 100644 index 0000000000000..2add5f59daf5d --- /dev/null +++ b/sound/hda/codecs/hdmi/nvhdmi.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nvidia HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +/* + * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: + * - 0x10de0015 + * - 0x10de0040 + */ +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + if (cap->ca_index == 0x00 && channels == 2) + return SNDRV_CTL_TLVT_CHMAP_FIXED; + + /* If the speaker allocation matches the channel count, it is OK. */ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + return 0; +} + +/* map from pin NID to port; port is 0-based */ +/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */ +static int nvhdmi_pin2port(void *audio_ptr, int pin_nid) +{ + return pin_nid - 4; +} + +/* reverse-map from port to pin NID: see above */ +static int nvhdmi_port2pin(struct hda_codec *codec, int port) +{ + return port + 4; +} + +static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { + .pin2port = nvhdmi_pin2port, + .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify, + .master_bind = snd_hda_hdmi_acomp_master_bind, + .master_unbind = snd_hda_hdmi_acomp_master_unbind, +}; + +static int patch_nvhdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + codec->dp_mst = true; + + spec = codec->spec; + + err = snd_hda_hdmi_parse_codec(codec); + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + snd_hda_hdmi_generic_init_per_pins(codec); + + spec->dyn_pin_out = true; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + codec->link_down_at_suspend = 1; + + snd_hda_hdmi_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin); + + return 0; +} + +static int patch_nvhdmi_legacy(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = patch_generic_hdmi(codec); + if (err) + return err; + + spec = codec->spec; + spec->dyn_pin_out = true; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + codec->link_down_at_suspend = 1; + + return 0; +} + +/* + * patch entries + */ +static const struct hda_device_id snd_hda_id_nvhdmi[] = { +HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy), +/* 17 is known to be absent */ +HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy), +HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009b, "GPU 9b HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009c, "GPU 9c HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a1, "GPU a1 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a8, "GPU a8 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a9, "GPU a9 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00aa, "GPU aa HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00ab, "GPU ab HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00ad, "GPU ad HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00ae, "GPU ae HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00af, "GPU af HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00b0, "GPU b0 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00b1, "GPU b1 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00c0, "GPU c0 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00c1, "GPU c1 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00c3, "GPU c3 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00c4, "GPU c4 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00c5, "GPU c5 HDMI/DP", patch_nvhdmi), +{} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver nvhdmi_driver = { + .id = snd_hda_id_nvhdmi, +}; + +module_hda_codec_driver(nvhdmi_driver); diff --git a/sound/hda/codecs/hdmi/simplehdmi.c b/sound/hda/codecs/hdmi/simplehdmi.c new file mode 100644 index 0000000000000..87ea997a6d3b0 --- /dev/null +++ b/sound/hda/codecs/hdmi/simplehdmi.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Non-generic simple HDMI codec support + */ + +#include +#include +#include "hdmi_local.h" +#include "hda_jack.h" + +int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hda_pcm *info; + unsigned int chans; + struct hda_pcm_stream *pstr; + struct hdmi_spec_per_cvt *per_cvt; + + per_cvt = get_cvt(spec, 0); + chans = get_wcaps(codec, per_cvt->cvt_nid); + chans = get_wcaps_channels(chans); + + info = snd_hda_codec_pcm_new(codec, "HDMI 0"); + if (!info) + return -ENOMEM; + spec->pcm_rec[0].pcm = info; + info->pcm_type = HDA_PCM_TYPE_HDMI; + pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; + *pstr = spec->pcm_playback; + pstr->nid = per_cvt->cvt_nid; + if (pstr->channels_max <= 2 && chans && chans <= 16) + pstr->channels_max = chans; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_pcms, "SND_HDA_CODEC_HDMI"); + +/* unsolicited event for jack sensing */ +void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_report_sync(codec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_unsol_event, "SND_HDA_CODEC_HDMI"); + +static void free_hdmi_jack_priv(struct snd_jack *jack) +{ + struct hdmi_pcm *pcm = jack->private_data; + + pcm->jack = NULL; +} + +static int simple_hdmi_build_jack(struct hda_codec *codec) +{ + char hdmi_str[32] = "HDMI/DP"; + struct hdmi_spec *spec = codec->spec; + struct snd_jack *jack; + struct hdmi_pcm *pcmp = get_hdmi_pcm(spec, 0); + int pcmdev = pcmp->pcm->device; + int err; + + if (pcmdev > 0) + sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); + + err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack, + true, false); + if (err < 0) + return err; + + pcmp->jack = jack; + jack->private_data = pcmp; + jack->private_free = free_hdmi_jack_priv; + return 0; +} + +int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int err; + + per_cvt = get_cvt(spec, 0); + err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid, + per_cvt->cvt_nid, + HDA_PCM_TYPE_HDMI); + if (err < 0) + return err; + return simple_hdmi_build_jack(codec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_controls, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_simple_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0); + hda_nid_t pin = per_pin->pin_nid; + + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* some codecs require to unmute the pin */ + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id); + return 0; +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_init, "SND_HDA_CODEC_HDMI"); + +void snd_hda_hdmi_simple_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + snd_array_free(&spec->pins); + snd_array_free(&spec->cvts); + kfree(spec); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_free, "SND_HDA_CODEC_HDMI"); + +int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->hw_constraints_channels) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + spec->hw_constraints_channels); + } else { + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + } + + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_pcm_open, "SND_HDA_CODEC_HDMI"); + +static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static const struct hda_pcm_stream simple_pcm_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = snd_hda_hdmi_simple_pcm_open, + .close = simple_playback_pcm_close, + .prepare = simple_playback_pcm_prepare + }, +}; + +static const struct hda_codec_ops simple_hdmi_patch_ops = { + .build_controls = snd_hda_hdmi_simple_build_controls, + .build_pcms = snd_hda_hdmi_simple_build_pcms, + .init = snd_hda_hdmi_simple_init, + .free = snd_hda_hdmi_simple_free, + .unsol_event = snd_hda_hdmi_simple_unsol_event, +}; + +int patch_simple_hdmi(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec; + struct hdmi_spec_per_cvt *per_cvt; + struct hdmi_spec_per_pin *per_pin; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + spec->codec = codec; + codec->spec = spec; + snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), 1); + snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), 1); + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = cvt_nid; + spec->num_cvts = 1; + spec->num_pins = 1; + per_pin = snd_array_new(&spec->pins); + per_cvt = snd_array_new(&spec->cvts); + if (!per_pin || !per_cvt) { + snd_hda_hdmi_simple_free(codec); + return -ENOMEM; + } + per_cvt->cvt_nid = cvt_nid; + per_pin->pin_nid = pin_nid; + spec->pcm_playback = simple_pcm_playback; + + codec->patch_ops = simple_hdmi_patch_ops; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(patch_simple_hdmi, "SND_HDA_CODEC_HDMI"); + +/* VIA HDMI Implementation */ +#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ +#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ + +static int patch_via_hdmi(struct hda_codec *codec) +{ + return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); +} + +/* + * patch entries + */ +static const struct hda_device_id snd_hda_id_simplehdmi[] = { +HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), +HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), +{} /* terminator */ +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simple HDMI HD-audio codec support"); + +static struct hda_codec_driver simplehdmi_driver = { + .id = snd_hda_id_simplehdmi, +}; + +module_hda_codec_driver(simplehdmi_driver); diff --git a/sound/hda/codecs/hdmi/tegrahdmi.c b/sound/hda/codecs/hdmi/tegrahdmi.c new file mode 100644 index 0000000000000..c13a637887996 --- /dev/null +++ b/sound/hda/codecs/hdmi/tegrahdmi.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Nvidia Tegra HDMI codec support + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hdmi_local.h" + +/* + * The HDA codec on NVIDIA Tegra contains two scratch registers that are + * accessed using vendor-defined verbs. These registers can be used for + * interoperability between the HDA and HDMI drivers. + */ + +/* Audio Function Group node */ +#define NVIDIA_AFG_NID 0x01 + +/* + * The SCRATCH0 register is used to notify the HDMI codec of changes in audio + * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to + * be raised in the HDMI codec. The remainder of the bits is arbitrary. This + * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an + * additional bit (at position 30) to signal the validity of the format. + * + * | 31 | 30 | 29 16 | 15 0 | + * +---------+-------+--------+--------+ + * | TRIGGER | VALID | UNUSED | FORMAT | + * +-----------------------------------| + * + * Note that for the trigger bit to take effect it needs to change value + * (i.e. it needs to be toggled). The trigger bit is not applicable from + * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt + * trigger to hdmi. + */ +#define NVIDIA_SET_HOST_INTR 0xf80 +#define NVIDIA_GET_SCRATCH0 0xfa6 +#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 +#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 +#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 +#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa +#define NVIDIA_SCRATCH_TRIGGER (1 << 7) +#define NVIDIA_SCRATCH_VALID (1 << 6) + +#define NVIDIA_GET_SCRATCH1 0xfab +#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac +#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad +#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae +#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf + +/* + * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, + * the format is invalidated so that the HDMI codec can be disabled. + */ +static void tegra_hdmi_set_format(struct hda_codec *codec, + hda_nid_t cvt_nid, + unsigned int format) +{ + unsigned int value; + unsigned int nid = NVIDIA_AFG_NID; + struct hdmi_spec *spec = codec->spec; + + /* + * Tegra HDA codec design from TEGRA234 chip onwards support DP MST. + * This resulted in moving scratch registers from audio function + * group to converter widget context. So CVT NID should be used for + * scratch register read/write for DP MST supported Tegra HDA codec. + */ + if (codec->dp_mst) + nid = cvt_nid; + + /* bits [31:30] contain the trigger and valid bits */ + value = snd_hda_codec_read(codec, nid, 0, + NVIDIA_GET_SCRATCH0, 0); + value = (value >> 24) & 0xff; + + /* bits [15:0] are used to store the HDA format */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE0, + (format >> 0) & 0xff); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE1, + (format >> 8) & 0xff); + + /* bits [16:24] are unused */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE2, 0); + + /* + * Bit 30 signals that the data is valid and hence that HDMI audio can + * be enabled. + */ + if (format == 0) + value &= ~NVIDIA_SCRATCH_VALID; + else + value |= NVIDIA_SCRATCH_VALID; + + if (spec->hdmi_intr_trig_ctrl) { + /* + * For Tegra HDA Codec design from TEGRA234 onwards, the + * Interrupt to hdmi driver is triggered by writing + * non-zero values to verb 0xF80 instead of 31st bit of + * scratch register. + */ + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_HOST_INTR, 0x1); + } else { + /* + * Whenever the 31st trigger bit is toggled, an interrupt is raised + * in the HDMI codec. The HDMI driver will use that as trigger + * to update its configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER; + + snd_hda_codec_write(codec, nid, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); + } +} + +static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int err; + + err = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag, + format, substream); + if (err < 0) + return err; + + /* notify the HDMI codec of the format change */ + tegra_hdmi_set_format(codec, hinfo->nid, format); + + return 0; +} + +static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + /* invalidate the format in the HDMI codec */ + tegra_hdmi_set_format(codec, hinfo->nid, 0); + + return snd_hda_hdmi_generic_pcm_cleanup(hinfo, codec, substream); +} + +static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < spec->num_pins; i++) { + struct hda_pcm *pcm = get_pcm_rec(spec, i); + + if (pcm->pcm_type == type) + return pcm; + } + + return NULL; +} + +static int tegra_hdmi_build_pcms(struct hda_codec *codec) +{ + struct hda_pcm_stream *stream; + struct hda_pcm *pcm; + int err; + + err = snd_hda_hdmi_generic_build_pcms(codec); + if (err < 0) + return err; + + pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); + if (!pcm) + return -ENODEV; + + /* + * Override ->prepare() and ->cleanup() operations to notify the HDMI + * codec about format changes. + */ + stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + stream->ops.prepare = tegra_hdmi_pcm_prepare; + stream->ops.cleanup = tegra_hdmi_pcm_cleanup; + + return 0; +} + +/* + * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: + * - 0x10de0015 + * - 0x10de0040 + */ +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + if (cap->ca_index == 0x00 && channels == 2) + return SNDRV_CTL_TLVT_CHMAP_FIXED; + + /* If the speaker allocation matches the channel count, it is OK. */ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) +{ + if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) + return -EINVAL; + + return 0; +} + +static int tegra_hdmi_init(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int i, err; + + err = snd_hda_hdmi_parse_codec(codec); + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + for (i = 0; i < spec->num_cvts; i++) + snd_hda_codec_write(codec, spec->cvt_nids[i], 0, + AC_VERB_SET_DIGI_CONVERT_1, + AC_DIG1_ENABLE); + + snd_hda_hdmi_generic_init_per_pins(codec); + + codec->depop_delay = 10; + codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + + spec->chmap.ops.chmap_cea_alloc_validate_get_type = + nvhdmi_chmap_cea_alloc_validate_get_type; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; + spec->nv_dp_workaround = true; + + return 0; +} + +static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + + return tegra_hdmi_init(codec); +} + +static int patch_tegra234_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + err = snd_hda_hdmi_generic_alloc(codec); + if (err < 0) + return err; + + codec->dp_mst = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->hdmi_intr_trig_ctrl = true; + + return tegra_hdmi_init(codec); +} + +/* + * patch entries + */ +static const struct hda_device_id snd_hda_id_tegrahdmi[] = { +HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), +HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), +HDA_CODEC_ENTRY(0x10de0033, "SoC 33 HDMI/DP", patch_tegra234_hdmi), +HDA_CODEC_ENTRY(0x10de0034, "Tegra264 HDMI/DP", patch_tegra234_hdmi), +HDA_CODEC_ENTRY(0x10de0035, "SoC 35 HDMI/DP", patch_tegra234_hdmi), +{} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec"); +MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); + +static struct hda_codec_driver tegrahdmi_driver = { + .id = snd_hda_id_tegrahdmi, +}; + +module_hda_codec_driver(tegrahdmi_driver); diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index 68c31f5354b7c..428aa5a06ead4 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -689,10 +689,6 @@ int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); -int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, - unsigned char *buf, int *eld_size, - bool rev3_or_later); - #ifdef CONFIG_SND_PROC_FS void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer, -- GitLab From 6bf917e9aacc360e76b74249209a4290f2e8404b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:12 +0200 Subject: [PATCH 640/868] ALSA: hda: Introduce hda_codec_driver ops Until now, we use "patch_ops" embedded in hda_codec object for defining the callbacks that are used in various places to manage HD-audio codec. But from the device driver POV, this should have been rather the driver ops, instead of the callbacks in the codec object. This patch defines the driver ops for HD-audio codec driver as the replacement. We reuse the same struct hda_codec_ops, and this is put as hda_codec_driver.ops. When the driver->ops callbacks are defined, they are called primarily instead of codec->patch_ops callbacks. With converting to the driver ops, there is no need to pass the ugly patch_ops handling in hda_device_id tables. That is, driver_data field of hda_device_id becomes really optional and it can be used for passing the codec-specific data (e.g. specifying a model). The codec entries after the conversion should be with HDA_CODEC_ID() and co, instead of the former HDA_CODEC_ENTRY(). Once after converting all codec drivers to use driver ops, we can get rid of codec patch_ops. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-10-tiwai@suse.de --- include/sound/hda_codec.h | 26 +++++++++++++++-- sound/hda/common/bind.c | 27 +++++++++++++---- sound/hda/common/codec.c | 56 ++++++++++++++++++++++++++---------- sound/hda/common/hda_local.h | 11 +++++++ sound/soc/codecs/hda.c | 27 +++++++++++------ sound/soc/codecs/hdac_hda.c | 28 +++++++++++++----- 6 files changed, 137 insertions(+), 38 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index c1fe6290d04dc..a725ac48c20c4 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -27,6 +27,7 @@ struct hda_beep; struct hda_codec; struct hda_pcm; struct hda_pcm_stream; +struct hda_codec_ops; /* * codec bus @@ -79,6 +80,17 @@ typedef int (*hda_codec_patch_t)(struct hda_codec *); #define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 #define HDA_CODEC_ID_GENERIC 0x00000201 +#define HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, _model) \ + { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ + .api_version = HDA_DEV_LEGACY, .driver_data = (_model) } +#define HDA_CODEC_ID_MODEL(_vid, _name, _model) \ + HDA_CODEC_ID_REV_MODEL(_vid, 0, _name, _model) +#define HDA_CODEC_ID_REV(_vid, _rev, _name) \ + HDA_CODEC_ID_REV_MODEL(_vid, _rev, _name, 0) +#define HDA_CODEC_ID(_vid, _name) \ + HDA_CODEC_ID_REV(_vid, 0, _name) + +/* old macros for patch_ops -- to be deprecated */ #define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \ { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ .api_version = HDA_DEV_LEGACY, \ @@ -89,8 +101,12 @@ typedef int (*hda_codec_patch_t)(struct hda_codec *); struct hda_codec_driver { struct hdac_driver core; const struct hda_device_id *id; + const struct hda_codec_ops *ops; }; +#define hda_codec_to_driver(codec) \ + container_of((codec)->core.dev.driver, struct hda_codec_driver, core.driver) + int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, struct module *owner); #define hda_codec_driver_register(drv) \ @@ -102,6 +118,8 @@ void hda_codec_driver_unregister(struct hda_codec_driver *drv); /* ops set by the preset patch */ struct hda_codec_ops { + int (*probe)(struct hda_codec *codec, const struct hda_device_id *id); + void (*remove)(struct hda_codec *codec); int (*build_controls)(struct hda_codec *codec); int (*build_pcms)(struct hda_codec *codec); int (*init)(struct hda_codec *codec); @@ -184,7 +202,7 @@ struct hda_codec { /* set by patch */ struct hda_codec_ops patch_ops; - /* PCM to create, set by patch_ops.build_pcms callback */ + /* PCM to create, set by hda_codec_ops.build_pcms callback */ struct list_head pcm_list_head; refcount_t pcm_ref; wait_queue_head_t remove_sleep; @@ -478,7 +496,11 @@ extern const struct dev_pm_ops hda_codec_driver_pm; static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - if (codec->patch_ops.check_power_status) + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + + if (driver->ops && driver->ops->check_power_status) + return driver->ops->check_power_status(codec, nid); + else if (codec->patch_ops.check_power_status) return codec->patch_ops.check_power_status(codec, nid); return 0; } diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c index df8f88beddd07..56975178f5339 100644 --- a/sound/hda/common/bind.c +++ b/sound/hda/common/bind.c @@ -42,6 +42,7 @@ static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *dr static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) { struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); /* ignore unsol events during shutdown */ if (codec->card->shutdown || codec->bus->shutdown) @@ -51,7 +52,9 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) if (codec->core.dev.power.power_state.event != PM_EVENT_ON) return; - if (codec->patch_ops.unsol_event) + if (driver->ops && driver->ops->unsol_event) + driver->ops->unsol_event(codec, ev); + else if (codec->patch_ops.unsol_event) codec->patch_ops.unsol_event(codec, ev); } @@ -87,6 +90,7 @@ static int hda_codec_driver_probe(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); struct module *owner = dev->driver->owner; + struct hda_codec_driver *driver = hda_codec_to_driver(codec); hda_codec_patch_t patch; int err; @@ -111,11 +115,17 @@ static int hda_codec_driver_probe(struct device *dev) goto error; } - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (patch) { - err = patch(codec); + if (driver->ops && driver->ops->probe) { + err = driver->ops->probe(codec, codec->preset); if (err < 0) goto error_module_put; + } else { + patch = (hda_codec_patch_t)codec->preset->driver_data; + if (patch) { + err = patch(codec); + if (err < 0) + goto error_module_put; + } } err = snd_hda_codec_build_pcms(codec); @@ -136,7 +146,9 @@ static int hda_codec_driver_probe(struct device *dev) return 0; error_module: - if (codec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(codec); + else if (codec->patch_ops.free) codec->patch_ops.free(codec); error_module_put: module_put(owner); @@ -150,6 +162,7 @@ static int hda_codec_driver_probe(struct device *dev) static int hda_codec_driver_remove(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); if (codec->bus->core.ext_ops) { if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) @@ -163,7 +176,9 @@ static int hda_codec_driver_remove(struct device *dev) wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); snd_power_sync_ref(codec->bus->card); - if (codec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(codec); + else if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); codec->preset = NULL; diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index cb72e9655c8a6..8899be764d689 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1114,6 +1114,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, int channel_id, int format) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hda_codec *c; struct hda_cvt_setup *p; int type; @@ -1129,7 +1130,9 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (!p) return; - if (codec->patch_ops.stream_pm) + if (driver->ops && driver->ops->stream_pm) + driver->ops->stream_pm(codec, nid, true); + else if (codec->patch_ops.stream_pm) codec->patch_ops.stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); @@ -1190,7 +1193,9 @@ EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream); static void really_cleanup_stream(struct hda_codec *codec, struct hda_cvt_setup *q) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); hda_nid_t nid = q->nid; + if (q->stream_tag || q->channel_id) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); if (q->format_id) @@ -1198,7 +1203,9 @@ static void really_cleanup_stream(struct hda_codec *codec, ); memset(q, 0, sizeof(*q)); q->nid = nid; - if (codec->patch_ops.stream_pm) + if (driver->ops && driver->ops->stream_pm) + driver->ops->stream_pm(codec, nid, false); + else if (codec->patch_ops.stream_pm) codec->patch_ops.stream_pm(codec, nid, false); } @@ -2746,6 +2753,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; int count; unsigned int state; @@ -2762,7 +2770,10 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* repeat power states setting at most 10 times*/ for (count = 0; count < 10; count++) { - if (codec->patch_ops.set_power_state) + /* might be called before binding to driver, too */ + if (driver && driver->ops && driver->ops->set_power_state) + driver->ops->set_power_state(codec, fg, power_state); + else if (codec->patch_ops.set_power_state) codec->patch_ops.set_power_state(codec, fg, power_state); else { @@ -2842,10 +2853,13 @@ void snd_hda_update_power_acct(struct hda_codec *codec) */ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); unsigned int state; snd_hdac_enter_pm(&codec->core); - if (codec->patch_ops.suspend) + if (driver->ops && driver->ops->suspend) + driver->ops->suspend(codec); + else if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); if (!codec->no_stream_clean_at_suspend) hda_cleanup_all_streams(codec); @@ -2860,6 +2874,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + snd_hdac_enter_pm(&codec->core); if (codec->core.regmap) regcache_mark_dirty(codec->core.regmap); @@ -2870,11 +2886,12 @@ static void hda_call_codec_resume(struct hda_codec *codec) restore_shutup_pins(codec); hda_exec_init_verbs(codec); snd_hda_jack_set_dirty_all(codec); - if (codec->patch_ops.resume) + if (driver->ops && driver->ops->resume) + driver->ops->resume(codec); + else if (codec->patch_ops.resume) codec->patch_ops.resume(codec); else { - if (codec->patch_ops.init) - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); } @@ -3059,15 +3076,20 @@ EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); int snd_hda_codec_build_controls(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); int err = 0; + hda_exec_init_verbs(codec); /* continue to initialize... */ - if (codec->patch_ops.init) - err = codec->patch_ops.init(codec); - if (!err && codec->patch_ops.build_controls) - err = codec->patch_ops.build_controls(codec); - if (err < 0) - return err; + err = snd_hda_codec_init(codec); + if (!err) { + if (driver->ops && driver->ops->build_controls) + err = driver->ops->build_controls(codec); + else if (codec->patch_ops.build_controls) + err = codec->patch_ops.build_controls(codec); + if (err < 0) + return err; + } /* we create chmaps here instead of build_pcms */ err = add_std_chmaps(codec); @@ -3253,16 +3275,20 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) /* call build_pcms ops of the given codec and set up the default parameters */ int snd_hda_codec_parse_pcms(struct hda_codec *codec) { + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hda_pcm *cpcm; int err; if (!list_empty(&codec->pcm_list_head)) return 0; /* already parsed */ - if (!codec->patch_ops.build_pcms) + if (driver->ops && driver->ops->build_pcms) + err = driver->ops->build_pcms(codec); + else if (codec->patch_ops.build_pcms) + err = codec->patch_ops.build_pcms(codec); + else return 0; - err = codec->patch_ops.build_pcms(codec); if (err < 0) { codec_err(codec, "cannot build PCMs for #%d (error %d)\n", codec->core.addr, err); diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index 428aa5a06ead4..654fe1156d568 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -652,6 +652,17 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, void snd_hda_codec_shutdown(struct hda_codec *codec); +static inline int snd_hda_codec_init(struct hda_codec *codec) +{ + struct hda_codec_driver *driver = hda_codec_to_driver(codec); + + if (driver->ops && driver->ops->init) + return driver->ops->init(codec); + else if (codec->patch_ops.init) + return codec->patch_ops.init(codec); + return 0; +} + /* * AMP control callbacks */ diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index dc7794c9ac44c..ddb31001657ea 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -173,6 +173,7 @@ EXPORT_SYMBOL_GPL(hda_codec_probe_complete); static int hda_codec_probe(struct snd_soc_component *component) { struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_device *hdev = &codec->core; struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink; @@ -214,14 +215,19 @@ static int hda_codec_probe(struct snd_soc_component *component) goto err; } - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (!patch) { - dev_err(&hdev->dev, "no patch specified\n"); - ret = -EINVAL; - goto err; + if (driver->ops && driver->ops->probe) { + ret = driver->ops->probe(codec, codec->preset); + } else { + patch = (hda_codec_patch_t)codec->preset->driver_data; + if (!patch) { + dev_err(&hdev->dev, "no patch specified\n"); + ret = -EINVAL; + goto err; + } + + ret = patch(codec); } - ret = patch(codec); if (ret < 0) { dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; @@ -252,7 +258,9 @@ static int hda_codec_probe(struct snd_soc_component *component) complete_err: hda_codec_unregister_dais(codec, component); parse_pcms_err: - if (codec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(codec); + else if (codec->patch_ops.free) codec->patch_ops.free(codec); err: snd_hda_codec_cleanup_for_unbind(codec); @@ -271,6 +279,7 @@ device_new_err: static void hda_codec_remove(struct snd_soc_component *component) { struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_device *hdev = &codec->core; struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink; @@ -281,7 +290,9 @@ static void hda_codec_remove(struct snd_soc_component *component) hda_codec_unregister_dais(codec, component); - if (codec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(codec); + else if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 29c88de5508b8..7bb7845d5e437 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -409,6 +409,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_soc_component_get_dapm(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *hcodec = hda_pvt->codec; + struct hda_codec_driver *driver = hda_codec_to_driver(hcodec); struct hdac_ext_link *hlink; hda_codec_patch_t patch; int ret; @@ -484,15 +485,23 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) goto error_pm; } - patch = (hda_codec_patch_t)hcodec->preset->driver_data; - if (patch) { - ret = patch(hcodec); + if (driver->ops && driver->ops->probe) { + ret = driver->ops->probe(hcodec, hcodec->preset); if (ret < 0) { - dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret); + dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret); goto error_regmap; } } else { - dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__); + patch = (hda_codec_patch_t)hcodec->preset->driver_data; + if (patch) { + ret = patch(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret); + goto error_regmap; + } + } else { + dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__); + } } ret = snd_hda_codec_parse_pcms(hcodec); @@ -531,7 +540,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) return 0; error_patch: - if (hcodec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(hcodec); + else if (hcodec->patch_ops.free) hcodec->patch_ops.free(hcodec); error_regmap: snd_hdac_regmap_exit(hdev); @@ -548,6 +559,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) snd_soc_component_get_drvdata(component); struct hdac_device *hdev = &hda_pvt->codec->core; struct hda_codec *codec = hda_pvt->codec; + struct hda_codec_driver *driver = hda_codec_to_driver(codec); struct hdac_ext_link *hlink = NULL; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); @@ -559,7 +571,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); - if (codec->patch_ops.free) + if (driver->ops && driver->ops->remove) + driver->ops->remove(codec); + else if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); -- GitLab From 1d0e6926ef8755d7a520c566d57eec1e4083ac8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:13 +0200 Subject: [PATCH 641/868] ALSA: hda/generic: Rewrite to new probe method Convert the generic HD-audio codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-11-tiwai@suse.de --- sound/hda/codecs/generic.c | 43 +++++++++++++++++++++++--------------- sound/hda/codecs/generic.h | 3 ++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 873fd4b7f4516..82c31b93424cf 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -4946,7 +4946,7 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) * @nid: audio widget * @on: power on/off flag * - * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. + * Set this in hda_codec_ops.stream_pm. Only valid with power_save_node flag. */ void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) { @@ -5230,7 +5230,7 @@ static const char * const follower_pfxs[] = { * snd_hda_gen_build_controls - Build controls from the parsed results * @codec: the HDA codec * - * Pass this to build_controls patch_ops. + * Pass this to build_controls hda_codec_ops. */ int snd_hda_gen_build_controls(struct hda_codec *codec) { @@ -5743,7 +5743,7 @@ static void setup_pcm_stream(struct hda_pcm_stream *str, * snd_hda_gen_build_pcms - build PCM streams based on the parsed results * @codec: the HDA codec * - * Pass this to build_pcms patch_ops. + * Pass this to build_pcms hda_codec_ops. */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { @@ -6032,7 +6032,7 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) * snd_hda_gen_init - initialize the generic spec * @codec: the HDA codec * - * This can be put as patch_ops init function. + * This can be put as hda_codec_ops init function. */ int snd_hda_gen_init(struct hda_codec *codec) { @@ -6070,26 +6070,26 @@ int snd_hda_gen_init(struct hda_codec *codec) EXPORT_SYMBOL_GPL(snd_hda_gen_init); /** - * snd_hda_gen_free - free the generic spec + * snd_hda_gen_remove - free the generic spec * @codec: the HDA codec * - * This can be put as patch_ops free function. + * This can be put as hda_codec_ops remove function. */ -void snd_hda_gen_free(struct hda_codec *codec) +void snd_hda_gen_remove(struct hda_codec *codec) { snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); snd_hda_gen_spec_free(codec->spec); kfree(codec->spec); codec->spec = NULL; } -EXPORT_SYMBOL_GPL(snd_hda_gen_free); +EXPORT_SYMBOL_GPL(snd_hda_gen_remove); /** * snd_hda_gen_check_power_status - check the loopback power save state * @codec: the HDA codec * @nid: NID to inspect * - * This can be put as patch_ops check_power_status function. + * This can be put as hda_codec_ops check_power_status function. */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -6112,11 +6112,8 @@ static const struct hda_codec_ops generic_patch_ops = { .check_power_status = snd_hda_gen_check_power_status, }; -/* - * snd_hda_parse_generic_codec - Generic codec parser - * @codec: the HDA codec - */ -static int snd_hda_parse_generic_codec(struct hda_codec *codec) +static int snd_hda_gen_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct hda_gen_spec *spec; int err; @@ -6139,19 +6136,31 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops generic_codec_ops = { + .probe = snd_hda_gen_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_generic[] = { - HDA_CODEC_ENTRY(0x1af40021, "Generic", snd_hda_parse_generic_codec), /* QEMU */ - HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec), + HDA_CODEC_ID(0x1af40021, "Generic"), /* QEMU */ + HDA_CODEC_ID(HDA_CODEC_ID_GENERIC, "Generic"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic); static struct hda_codec_driver generic_driver = { .id = snd_hda_id_generic, + .ops = &generic_codec_ops, }; module_hda_codec_driver(generic_driver); diff --git a/sound/hda/codecs/generic.h b/sound/hda/codecs/generic.h index 9612afaa61c20..00a92fc558462 100644 --- a/sound/hda/codecs/generic.h +++ b/sound/hda/codecs/generic.h @@ -311,7 +311,8 @@ enum { int snd_hda_gen_spec_init(struct hda_gen_spec *spec); int snd_hda_gen_init(struct hda_codec *codec); -void snd_hda_gen_free(struct hda_codec *codec); +void snd_hda_gen_remove(struct hda_codec *codec); +#define snd_hda_gen_free snd_hda_gen_remove int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); -- GitLab From e1d695b45fd112810b4621f7e1bb48d197f5ef6a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:14 +0200 Subject: [PATCH 642/868] ALSA: hda/realtek: Rewrite to new probe method Convert the Realtek codec drivers to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-12-tiwai@suse.de --- sound/hda/codecs/realtek/alc260.c | 20 ++++- sound/hda/codecs/realtek/alc262.c | 20 ++++- sound/hda/codecs/realtek/alc268.c | 22 +++++- sound/hda/codecs/realtek/alc269.c | 111 +++++++++++++++------------- sound/hda/codecs/realtek/alc662.c | 42 +++++++---- sound/hda/codecs/realtek/alc680.c | 20 ++++- sound/hda/codecs/realtek/alc861.c | 22 +++++- sound/hda/codecs/realtek/alc861vd.c | 22 +++++- sound/hda/codecs/realtek/alc880.c | 24 ++++-- sound/hda/codecs/realtek/alc882.c | 48 +++++++----- sound/hda/codecs/realtek/realtek.c | 17 +---- sound/hda/codecs/realtek/realtek.h | 4 - 12 files changed, 244 insertions(+), 128 deletions(-) diff --git a/sound/hda/codecs/realtek/alc260.c b/sound/hda/codecs/realtek/alc260.c index ebe20eaec58a4..8bd47079dccb3 100644 --- a/sound/hda/codecs/realtek/alc260.c +++ b/sound/hda/codecs/realtek/alc260.c @@ -211,7 +211,7 @@ static const struct hda_model_fixup alc260_fixup_models[] = { /* */ -static int patch_alc260(struct hda_codec *codec) +static int alc260_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -252,15 +252,28 @@ static int patch_alc260(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc260_codec_ops = { + .probe = alc260_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc260[] = { - HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260), + HDA_CODEC_ID(0x10ec0260, "ALC260"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260); @@ -271,6 +284,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc260_driver = { .id = snd_hda_id_alc260, + .ops = &alc260_codec_ops, }; module_hda_codec_driver(alc260_driver); diff --git a/sound/hda/codecs/realtek/alc262.c b/sound/hda/codecs/realtek/alc262.c index ffe61f447667f..3ec06cf5d2a61 100644 --- a/sound/hda/codecs/realtek/alc262.c +++ b/sound/hda/codecs/realtek/alc262.c @@ -126,7 +126,7 @@ static const struct hda_model_fixup alc262_fixup_models[] = { /* */ -static int patch_alc262(struct hda_codec *codec) +static int alc262_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -175,15 +175,28 @@ static int patch_alc262(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc262_codec_ops = { + .probe = alc262_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc262[] = { - HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262), + HDA_CODEC_ID(0x10ec0262, "ALC262"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc262); @@ -194,6 +207,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc262_driver = { .id = snd_hda_id_alc262, + .ops = &alc262_codec_ops, }; module_hda_codec_driver(alc262_driver); diff --git a/sound/hda/codecs/realtek/alc268.c b/sound/hda/codecs/realtek/alc268.c index d018f9982feb3..e489cdc98eb88 100644 --- a/sound/hda/codecs/realtek/alc268.c +++ b/sound/hda/codecs/realtek/alc268.c @@ -101,7 +101,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) /* */ -static int patch_alc268(struct hda_codec *codec) +static int alc268_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int i, err; @@ -151,16 +151,29 @@ static int patch_alc268(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc268_codec_ops = { + .probe = alc268_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc268[] = { - HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), - HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), + HDA_CODEC_ID(0x10ec0267, "ALC267"), + HDA_CODEC_ID(0x10ec0268, "ALC268"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268); @@ -171,6 +184,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc268_driver = { .id = snd_hda_id_alc268, + .ops = &alc268_codec_ops, }; module_hda_codec_driver(alc268_driver); diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 13549b5b9c428..03daa58a88de6 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -992,7 +992,7 @@ static int alc269_resume(struct hda_codec *codec) msleep(150); } - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); if (spec->codec_variant == ALC269_TYPE_ALC269VB) alc269vb_toggle_power_output(codec, 1); @@ -7842,19 +7842,19 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_update_coef_idx(codec, 0x4, 0, 1<<11); } -static void alc269_free(struct hda_codec *codec) +static void alc269_remove(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; if (spec) hda_component_manager_free(&spec->comps, &comp_master_ops); - alc_free(codec); + snd_hda_gen_remove(codec); } /* */ -static int patch_alc269(struct hda_codec *codec) +static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -7868,9 +7868,6 @@ static int patch_alc269(struct hda_codec *codec) codec->power_save_node = 0; spec->en_3kpull_low = true; - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; - codec->patch_ops.free = alc269_free; spec->shutup = alc_default_shutup; spec->init_hook = alc_default_init; @@ -8068,56 +8065,69 @@ static int patch_alc269(struct hda_codec *codec) return 0; error: - alc_free(codec); + alc269_remove(codec); return err; } +static const struct hda_codec_ops alc269_codec_ops = { + .probe = alc269_probe, + .remove = alc269_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = alc269_suspend, + .resume = alc269_resume, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc269[] = { - HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0230, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0300, "ALC300", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0623, "ALC623", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269), - HDA_CODEC_ENTRY(0x10ec0711, "ALC711", patch_alc269), - HDA_CODEC_ENTRY(0x19e58326, "HW8326", patch_alc269), + HDA_CODEC_ID(0x10ec0215, "ALC215"), + HDA_CODEC_ID(0x10ec0221, "ALC221"), + HDA_CODEC_ID(0x10ec0222, "ALC222"), + HDA_CODEC_ID(0x10ec0225, "ALC225"), + HDA_CODEC_ID(0x10ec0230, "ALC236"), + HDA_CODEC_ID(0x10ec0231, "ALC231"), + HDA_CODEC_ID(0x10ec0233, "ALC233"), + HDA_CODEC_ID(0x10ec0234, "ALC234"), + HDA_CODEC_ID(0x10ec0235, "ALC233"), + HDA_CODEC_ID(0x10ec0236, "ALC236"), + HDA_CODEC_ID(0x10ec0245, "ALC245"), + HDA_CODEC_ID(0x10ec0255, "ALC255"), + HDA_CODEC_ID(0x10ec0256, "ALC256"), + HDA_CODEC_ID(0x10ec0257, "ALC257"), + HDA_CODEC_ID(0x10ec0269, "ALC269"), + HDA_CODEC_ID(0x10ec0270, "ALC270"), + HDA_CODEC_ID(0x10ec0274, "ALC274"), + HDA_CODEC_ID(0x10ec0275, "ALC275"), + HDA_CODEC_ID(0x10ec0276, "ALC276"), + HDA_CODEC_ID(0x10ec0280, "ALC280"), + HDA_CODEC_ID(0x10ec0282, "ALC282"), + HDA_CODEC_ID(0x10ec0283, "ALC283"), + HDA_CODEC_ID(0x10ec0284, "ALC284"), + HDA_CODEC_ID(0x10ec0285, "ALC285"), + HDA_CODEC_ID(0x10ec0286, "ALC286"), + HDA_CODEC_ID(0x10ec0287, "ALC287"), + HDA_CODEC_ID(0x10ec0288, "ALC288"), + HDA_CODEC_ID(0x10ec0289, "ALC289"), + HDA_CODEC_ID(0x10ec0290, "ALC290"), + HDA_CODEC_ID(0x10ec0292, "ALC292"), + HDA_CODEC_ID(0x10ec0293, "ALC293"), + HDA_CODEC_ID(0x10ec0294, "ALC294"), + HDA_CODEC_ID(0x10ec0295, "ALC295"), + HDA_CODEC_ID(0x10ec0298, "ALC298"), + HDA_CODEC_ID(0x10ec0299, "ALC299"), + HDA_CODEC_ID(0x10ec0300, "ALC300"), + HDA_CODEC_ID(0x10ec0623, "ALC623"), + HDA_CODEC_ID(0x10ec0700, "ALC700"), + HDA_CODEC_ID(0x10ec0701, "ALC701"), + HDA_CODEC_ID(0x10ec0703, "ALC703"), + HDA_CODEC_ID(0x10ec0711, "ALC711"), + HDA_CODEC_ID(0x19e58326, "HW8326"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc269); @@ -8129,6 +8139,7 @@ MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); static struct hda_codec_driver alc269_driver = { .id = snd_hda_id_alc269, + .ops = &alc269_codec_ops, }; module_hda_codec_driver(alc269_driver); diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 94e4a313e5e2e..5073165d1f3cf 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -994,7 +994,7 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { /* */ -static int patch_alc662(struct hda_codec *codec) +static int alc662_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -1067,26 +1067,39 @@ static int patch_alc662(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc662_codec_ops = { + .probe = alc662_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc662[] = { - HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662), - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), - HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), + HDA_CODEC_ID(0x10ec0272, "ALC272"), + HDA_CODEC_ID_REV(0x10ec0662, 0x100101, "ALC662 rev1"), + HDA_CODEC_ID_REV(0x10ec0662, 0x100300, "ALC662 rev3"), + HDA_CODEC_ID(0x10ec0663, "ALC663"), + HDA_CODEC_ID(0x10ec0665, "ALC665"), + HDA_CODEC_ID(0x10ec0667, "ALC667"), + HDA_CODEC_ID(0x10ec0668, "ALC668"), + HDA_CODEC_ID(0x10ec0670, "ALC670"), + HDA_CODEC_ID(0x10ec0671, "ALC671"), + HDA_CODEC_ID(0x10ec0867, "ALC891"), + HDA_CODEC_ID(0x10ec0892, "ALC892"), + HDA_CODEC_ID(0x10ec0897, "ALC897"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc662); @@ -1097,6 +1110,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc662_driver = { .id = snd_hda_id_alc662, + .ops = &alc662_codec_ops, }; module_hda_codec_driver(alc662_driver); diff --git a/sound/hda/codecs/realtek/alc680.c b/sound/hda/codecs/realtek/alc680.c index baf19ee3bc2f4..8aab1026243c0 100644 --- a/sound/hda/codecs/realtek/alc680.c +++ b/sound/hda/codecs/realtek/alc680.c @@ -14,7 +14,7 @@ static int alc680_parse_auto_config(struct hda_codec *codec) /* */ -static int patch_alc680(struct hda_codec *codec) +static int alc680_probe(struct hda_codec *codec, const struct hda_device_id *id) { int err; @@ -26,18 +26,31 @@ static int patch_alc680(struct hda_codec *codec) /* automatic parse from the BIOS config */ err = alc680_parse_auto_config(codec); if (err < 0) { - alc_free(codec); + snd_hda_gen_remove(codec); return err; } return 0; } +static const struct hda_codec_ops alc680_codec_ops = { + .probe = alc680_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc680[] = { - HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680), + HDA_CODEC_ID(0x10ec0680, "ALC680"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc680); @@ -48,6 +61,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc680_driver = { .id = snd_hda_id_alc680, + .ops = &alc680_codec_ops, }; module_hda_codec_driver(alc680_driver); diff --git a/sound/hda/codecs/realtek/alc861.c b/sound/hda/codecs/realtek/alc861.c index 0eb70b44dc057..270037c6504ac 100644 --- a/sound/hda/codecs/realtek/alc861.c +++ b/sound/hda/codecs/realtek/alc861.c @@ -88,7 +88,7 @@ static const struct hda_quirk alc861_fixup_tbl[] = { /* */ -static int patch_alc861(struct hda_codec *codec) +static int alc861_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -124,16 +124,29 @@ static int patch_alc861(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc861_codec_ops = { + .probe = alc861_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc861[] = { - HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), - HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), + HDA_CODEC_ID_REV(0x10ec0861, 0x100340, "ALC660"), + HDA_CODEC_ID(0x10ec0861, "ALC861"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861); @@ -144,6 +157,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc861_driver = { .id = snd_hda_id_alc861, + .ops = &alc861_codec_ops, }; module_hda_codec_driver(alc861_driver); diff --git a/sound/hda/codecs/realtek/alc861vd.c b/sound/hda/codecs/realtek/alc861vd.c index ebf274ad730b4..44264e0d6e562 100644 --- a/sound/hda/codecs/realtek/alc861vd.c +++ b/sound/hda/codecs/realtek/alc861vd.c @@ -62,7 +62,7 @@ static const struct hda_quirk alc861vd_fixup_tbl[] = { /* */ -static int patch_alc861vd(struct hda_codec *codec) +static int alc861vd_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -98,16 +98,29 @@ static int patch_alc861vd(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc861vd_codec_ops = { + .probe = alc861vd_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc861vd[] = { - HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), - HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd), + HDA_CODEC_ID(0x10ec0660, "ALC660-VD"), + HDA_CODEC_ID(0x10ec0862, "ALC861-VD"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861vd); @@ -118,6 +131,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc861vd_driver = { .id = snd_hda_id_alc861vd, + .ops = &alc861vd_codec_ops, }; module_hda_codec_driver(alc861vd_driver); diff --git a/sound/hda/codecs/realtek/alc880.c b/sound/hda/codecs/realtek/alc880.c index 2e828ab96dc51..bf1bdf11ec2d1 100644 --- a/sound/hda/codecs/realtek/alc880.c +++ b/sound/hda/codecs/realtek/alc880.c @@ -434,9 +434,9 @@ static const struct hda_model_fixup alc880_fixup_models[] = { /* - * OK, here we have finally the patch for ALC880 + * OK, here we have finally the probe for ALC880 */ -static int patch_alc880(struct hda_codec *codec) +static int alc880_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -449,8 +449,6 @@ static int patch_alc880(struct hda_codec *codec) spec->gen.need_dac_fix = 1; spec->gen.beep_nid = 0x01; - codec->patch_ops.unsol_event = alc880_unsol_event; - alc_pre_init(codec); snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, @@ -473,15 +471,28 @@ static int patch_alc880(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc880_codec_ops = { + .probe = alc880_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = alc880_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc880[] = { - HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880), + HDA_CODEC_ID(0x10ec0880, "ALC880"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880); @@ -492,6 +503,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc880_driver = { .id = snd_hda_id_alc880, + .ops = &alc880_codec_ops, }; module_hda_codec_driver(alc880_driver); diff --git a/sound/hda/codecs/realtek/alc882.c b/sound/hda/codecs/realtek/alc882.c index 6af2f7fcc6bb3..529fecd5baa0a 100644 --- a/sound/hda/codecs/realtek/alc882.c +++ b/sound/hda/codecs/realtek/alc882.c @@ -757,7 +757,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* */ -static int patch_alc882(struct hda_codec *codec) +static int alc882_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct alc_spec *spec; int err; @@ -809,29 +809,42 @@ static int patch_alc882(struct hda_codec *codec) return 0; error: - alc_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops alc882_codec_ops = { + .probe = alc882_probe, + .remove = snd_hda_gen_remove, + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .unsol_event = snd_hda_jack_unsol_event, + .resume = alc_resume, + .suspend = alc_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_alc882[] = { - HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882), - HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), - HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1168, "ALC1220", patch_alc882), - HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), + HDA_CODEC_ID_REV(0x10ec0662, 0x100002, "ALC662 rev2"), + HDA_CODEC_ID(0x10ec0882, "ALC882"), + HDA_CODEC_ID(0x10ec0883, "ALC883"), + HDA_CODEC_ID_REV(0x10ec0885, 0x100101, "ALC889A"), + HDA_CODEC_ID_REV(0x10ec0885, 0x100103, "ALC889A"), + HDA_CODEC_ID(0x10ec0885, "ALC885"), + HDA_CODEC_ID(0x10ec0887, "ALC887"), + HDA_CODEC_ID_REV(0x10ec0888, 0x100101, "ALC1200"), + HDA_CODEC_ID(0x10ec0888, "ALC888"), + HDA_CODEC_ID(0x10ec0889, "ALC889"), + HDA_CODEC_ID(0x10ec0899, "ALC898"), + HDA_CODEC_ID(0x10ec0900, "ALC1150"), + HDA_CODEC_ID(0x10ec0b00, "ALCS1200A"), + HDA_CODEC_ID(0x10ec1168, "ALC1220"), + HDA_CODEC_ID(0x10ec1220, "ALC1220"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc882); @@ -842,6 +855,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); static struct hda_codec_driver alc882_driver = { .id = snd_hda_id_alc882, + .ops = &alc882_codec_ops, }; module_hda_codec_driver(alc882_driver); diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index 4ab49e76c304a..66b2efb9acb37 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -862,27 +862,13 @@ int alc_resume(struct hda_codec *codec) if (!spec->no_depop_delay) msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); hda_call_check_power_status(codec, 0x01); return 0; } EXPORT_SYMBOL_NS_GPL(alc_resume, "SND_HDA_CODEC_REALTEK"); -/* - */ -const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, - .resume = alc_resume, - .suspend = alc_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; -EXPORT_SYMBOL_NS_GPL(alc_patch_ops, "SND_HDA_CODEC_REALTEK"); - /* * Rename codecs appropriately from COEF value or subvendor id */ @@ -1082,7 +1068,6 @@ int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) /* FIXME: do we need this for all Realtek codec models? */ codec->spdif_status_reset = 1; codec->forced_resume = 1; - codec->patch_ops = alc_patch_ops; mutex_init(&spec->coef_mutex); err = alc_codec_rename_from_preset(codec); diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h index ac142f2540e3f..ee893da0c486a 100644 --- a/sound/hda/codecs/realtek/realtek.h +++ b/sound/hda/codecs/realtek/realtek.h @@ -226,15 +226,11 @@ void alc_power_eapd(struct hda_codec *codec); int alc_suspend(struct hda_codec *codec); int alc_resume(struct hda_codec *codec); -#define alc_free snd_hda_gen_free - int alc_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ignore_nids, const hda_nid_t *ssid_nids); int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid); -extern const struct hda_codec_ops alc_patch_ops; - #define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name) #ifdef CONFIG_SND_HDA_INPUT_BEEP -- GitLab From 0f1e8306dcbef54f64368aefaff175d5a135671b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:15 +0200 Subject: [PATCH 643/868] ALSA: hda/cmedia: Rewrite to new probe method Convert the C-Media codec driver to use the new hda_codec_ops probe. Since the CM9825 uses a completely different probe and codec ops, factor out to an individual codec driver, snd-hda-codec-cm9825. Other than that, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-13-tiwai@suse.de --- sound/hda/codecs/Kconfig | 10 + sound/hda/codecs/Makefile | 2 + sound/hda/codecs/cm9825.c | 312 ++++++++++++++++++++++++++++++++ sound/hda/codecs/cmedia.c | 372 +++++--------------------------------- 4 files changed, 365 insertions(+), 331 deletions(-) create mode 100644 sound/hda/codecs/cm9825.c diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig index e8c2efd2efb6e..addbc94243365 100644 --- a/sound/hda/codecs/Kconfig +++ b/sound/hda/codecs/Kconfig @@ -99,6 +99,16 @@ config SND_HDA_CODEC_CMEDIA comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m +config SND_HDA_CODEC_CM9825 + tristate "Build C-Media CM9825 HD-audio codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include C-Media CM9825 HD-audio codec support in + snd-hda-intel driver + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CM9825=m + config SND_HDA_CODEC_SI3054 tristate "Build Silicon Labs 3054 HD-modem codec support" help diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile index 14e5041aa4f07..e7f03e281999f 100644 --- a/sound/hda/codecs/Makefile +++ b/sound/hda/codecs/Makefile @@ -3,6 +3,7 @@ subdir-ccflags-y += -I$(src)/../common snd-hda-codec-generic-y := generic.o snd-hda-codec-cmedia-y := cmedia.o +snd-hda-codec-cm9825-y := cm9825.o snd-hda-codec-analog-y := analog.o snd-hda-codec-ca0110-y := ca0110.o snd-hda-codec-ca0132-y := ca0132.o @@ -21,6 +22,7 @@ obj-y += side-codecs/ # codec drivers obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o +obj-$(CONFIG_SND_HDA_CODEC_CM9825) += snd-hda-codec-cm9825.o obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c new file mode 100644 index 0000000000000..5c474ce44348f --- /dev/null +++ b/sound/hda/codecs/cm9825.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CM9825 HD-audio codec + */ + +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "generic.h" + +/* CM9825 Offset Definitions */ + +#define CM9825_VERB_SET_HPF_1 0x781 +#define CM9825_VERB_SET_HPF_2 0x785 +#define CM9825_VERB_SET_PLL 0x7a0 +#define CM9825_VERB_SET_NEG 0x7a1 +#define CM9825_VERB_SET_ADCL 0x7a2 +#define CM9825_VERB_SET_DACL 0x7a3 +#define CM9825_VERB_SET_MBIAS 0x7a4 +#define CM9825_VERB_SET_VNEG 0x7a8 +#define CM9825_VERB_SET_D2S 0x7a9 +#define CM9825_VERB_SET_DACTRL 0x7aa +#define CM9825_VERB_SET_PDNEG 0x7ac +#define CM9825_VERB_SET_VDO 0x7ad +#define CM9825_VERB_SET_CDALR 0x7b0 +#define CM9825_VERB_SET_MTCBA 0x7b1 +#define CM9825_VERB_SET_OTP 0x7b2 +#define CM9825_VERB_SET_OCP 0x7b3 +#define CM9825_VERB_SET_GAD 0x7b4 +#define CM9825_VERB_SET_TMOD 0x7b5 +#define CM9825_VERB_SET_SNR 0x7b6 + +struct cmi_spec { + struct hda_gen_spec gen; + const struct hda_verb *chip_d0_verbs; + const struct hda_verb *chip_d3_verbs; + const struct hda_verb *chip_hp_present_verbs; + const struct hda_verb *chip_hp_remove_verbs; + struct hda_codec *codec; + struct delayed_work unsol_hp_work; + int quirk; +}; + +static const struct hda_verb cm9825_std_d3_verbs[] = { + /* chip sleep verbs */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */ + {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */ + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ + {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */ + {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */ + {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ + {} +}; + +static const struct hda_verb cm9825_std_d0_verbs[] = { + /* chip init verbs */ + {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */ + {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */ + {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */ + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */ + {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ + {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */ + {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ + {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */ + {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */ + {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */ + {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */ + {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | + AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */ + {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | + AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */ + {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */ + {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */ + {} +}; + +static const struct hda_verb cm9825_hp_present_verbs[] = { + {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */ + {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */ + {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */ + {} +}; + +static const struct hda_verb cm9825_hp_remove_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ + {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */ + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ + {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ + {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */ + {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ + {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */ + {} +}; + +static void cm9825_unsol_hp_delayed(struct work_struct *work) +{ + struct cmi_spec *spec = + container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work); + struct hda_jack_tbl *jack; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_jack_plugin = false; + int err = 0; + + hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); + + codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", + (int)hp_jack_plugin, hp_pin); + + if (!hp_jack_plugin) { + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); + if (err) + codec_dbg(spec->codec, "codec_write err %d\n", err); + + snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs); + } else { + snd_hda_sequence_write(spec->codec, + spec->chip_hp_present_verbs); + } + + jack = snd_hda_jack_tbl_get(spec->codec, hp_pin); + if (jack) { + jack->block_report = 0; + snd_hda_jack_report_sync(spec->codec); + } +} + +static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) +{ + struct cmi_spec *spec = codec->spec; + struct hda_jack_tbl *tbl; + + /* Delay enabling the HP amp, to let the mic-detection + * state machine run. + */ + + codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid); + + tbl = snd_hda_jack_tbl_get(codec, cb->nid); + if (tbl) + tbl->block_report = 1; + schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200)); +} + +static void cm9825_setup_unsol(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + + snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback); +} + +static int cm9825_init(struct hda_codec *codec) +{ + snd_hda_gen_init(codec); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + + return 0; +} + +static void cm9825_remove(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + snd_hda_gen_remove(codec); +} + +static int cm9825_suspend(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + + snd_hda_sequence_write(codec, spec->chip_d3_verbs); + + return 0; +} + +static int cm9825_resume(struct hda_codec *codec) +{ + struct cmi_spec *spec = codec->spec; + hda_nid_t hp_pin = 0; + bool hp_jack_plugin = false; + int err; + + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); + if (err) + codec_dbg(codec, "codec_write err %d\n", err); + + msleep(150); /* for depop noise */ + + snd_hda_codec_init(codec); + + hp_pin = spec->gen.autocfg.hp_pins[0]; + hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); + + codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", + (int)hp_jack_plugin, hp_pin); + + if (!hp_jack_plugin) { + err = + snd_hda_codec_write(spec->codec, 0x42, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); + + if (err) + codec_dbg(codec, "codec_write err %d\n", err); + + snd_hda_sequence_write(codec, cm9825_hp_remove_verbs); + } + + snd_hda_regmap_sync(codec); + hda_call_check_power_status(codec, 0x01); + + return 0; +} + +static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct cmi_spec *spec; + struct auto_pin_cfg *cfg; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); + codec->spec = spec; + spec->codec = codec; + cfg = &spec->gen.autocfg; + snd_hda_gen_spec_init(&spec->gen); + spec->chip_d0_verbs = cm9825_std_d0_verbs; + spec->chip_d3_verbs = cm9825_std_d3_verbs; + spec->chip_hp_present_verbs = cm9825_hp_present_verbs; + spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; + + snd_hda_sequence_write(codec, spec->chip_d0_verbs); + + err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); + if (err < 0) + goto error; + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + goto error; + + cm9825_setup_unsol(codec); + + return 0; + + error: + cm9825_remove(codec); + + codec_info(codec, "Enter err %d\n", err); + + return err; +} + +static const struct hda_codec_ops cm9825_codec_ops = { + .probe = cm9825_probe, + .remove = cm9825_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cm9825_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cm9825_suspend, + .resume = cm9825_resume, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_cm9825[] = { + HDA_CODEC_ID(0x13f69825, "CM9825"), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cm9825); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CM9825 HD-audio codec"); + +static struct hda_codec_driver cm9825_driver = { + .id = snd_hda_id_cm9825, + .ops = &cm9825_codec_ops, +}; + +module_hda_codec_driver(cm9825_driver); diff --git a/sound/hda/codecs/cmedia.c b/sound/hda/codecs/cmedia.c index c88da2f8233ea..15e5a1118a6e8 100644 --- a/sound/hda/codecs/cmedia.c +++ b/sound/hda/codecs/cmedia.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Universal Interface for Intel High Definition Audio Codec + * Universal codec driver for Intel High Definition Audio Codec * - * HD audio interface patch for C-Media CMI9880 + * HD audio codec driver for C-Media CMI9880 * * Copyright (c) 2004 Takashi Iwai */ @@ -17,303 +17,27 @@ #include "hda_jack.h" #include "generic.h" -/* CM9825 Offset Definitions */ - -#define CM9825_VERB_SET_HPF_1 0x781 -#define CM9825_VERB_SET_HPF_2 0x785 -#define CM9825_VERB_SET_PLL 0x7a0 -#define CM9825_VERB_SET_NEG 0x7a1 -#define CM9825_VERB_SET_ADCL 0x7a2 -#define CM9825_VERB_SET_DACL 0x7a3 -#define CM9825_VERB_SET_MBIAS 0x7a4 -#define CM9825_VERB_SET_VNEG 0x7a8 -#define CM9825_VERB_SET_D2S 0x7a9 -#define CM9825_VERB_SET_DACTRL 0x7aa -#define CM9825_VERB_SET_PDNEG 0x7ac -#define CM9825_VERB_SET_VDO 0x7ad -#define CM9825_VERB_SET_CDALR 0x7b0 -#define CM9825_VERB_SET_MTCBA 0x7b1 -#define CM9825_VERB_SET_OTP 0x7b2 -#define CM9825_VERB_SET_OCP 0x7b3 -#define CM9825_VERB_SET_GAD 0x7b4 -#define CM9825_VERB_SET_TMOD 0x7b5 -#define CM9825_VERB_SET_SNR 0x7b6 - -struct cmi_spec { - struct hda_gen_spec gen; - const struct hda_verb *chip_d0_verbs; - const struct hda_verb *chip_d3_verbs; - const struct hda_verb *chip_hp_present_verbs; - const struct hda_verb *chip_hp_remove_verbs; - struct hda_codec *codec; - struct delayed_work unsol_hp_work; - int quirk; -}; - -static const struct hda_verb cm9825_std_d3_verbs[] = { - /* chip sleep verbs */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */ - {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */ - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ - {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */ - {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */ - {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ - {} -}; - -static const struct hda_verb cm9825_std_d0_verbs[] = { - /* chip init verbs */ - {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */ - {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */ - {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */ - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */ - {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ - {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */ - {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */ - {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */ - {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */ - {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */ - {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */ - {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | - AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */ - {0x3C, AC_VERB_SET_AMP_GAIN_MUTE | - AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */ - {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */ - {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */ - {} -}; - -static const struct hda_verb cm9825_hp_present_verbs[] = { - {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */ - {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */ - {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */ - {} -}; - -static const struct hda_verb cm9825_hp_remove_verbs[] = { - {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */ - {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */ - {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */ - {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */ - {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */ - {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */ - {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */ - {} -}; - -static void cm9825_unsol_hp_delayed(struct work_struct *work) -{ - struct cmi_spec *spec = - container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work); - struct hda_jack_tbl *jack; - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - bool hp_jack_plugin = false; - int err = 0; - - hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); - - codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", - (int)hp_jack_plugin, hp_pin); - - if (!hp_jack_plugin) { - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); - if (err) - codec_dbg(spec->codec, "codec_write err %d\n", err); - - snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs); - } else { - snd_hda_sequence_write(spec->codec, - spec->chip_hp_present_verbs); - } - - jack = snd_hda_jack_tbl_get(spec->codec, hp_pin); - if (jack) { - jack->block_report = 0; - snd_hda_jack_report_sync(spec->codec); - } -} - -static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) -{ - struct cmi_spec *spec = codec->spec; - struct hda_jack_tbl *tbl; - - /* Delay enabling the HP amp, to let the mic-detection - * state machine run. - */ - - codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid); - - tbl = snd_hda_jack_tbl_get(codec, cb->nid); - if (tbl) - tbl->block_report = 1; - schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200)); -} - -static void cm9825_setup_unsol(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; - - snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback); -} - -static int cm9825_init(struct hda_codec *codec) -{ - snd_hda_gen_init(codec); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); - - return 0; -} - -static void cm9825_free(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - snd_hda_gen_free(codec); -} - -static int cm9825_suspend(struct hda_codec *codec) +static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id) { - struct cmi_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - - snd_hda_sequence_write(codec, spec->chip_d3_verbs); - - return 0; -} - -static int cm9825_resume(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - hda_nid_t hp_pin = 0; - bool hp_jack_plugin = false; - int err; - - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); - if (err) - codec_dbg(codec, "codec_write err %d\n", err); - - msleep(150); /* for depop noise */ - - codec->patch_ops.init(codec); - - hp_pin = spec->gen.autocfg.hp_pins[0]; - hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin); - - codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n", - (int)hp_jack_plugin, hp_pin); - - if (!hp_jack_plugin) { - err = - snd_hda_codec_write(spec->codec, 0x42, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); - - if (err) - codec_dbg(codec, "codec_write err %d\n", err); - - snd_hda_sequence_write(codec, cm9825_hp_remove_verbs); - } - - snd_hda_regmap_sync(codec); - hda_call_check_power_status(codec, 0x01); - - return 0; -} - -/* - * stuff for auto-parser - */ -static const struct hda_codec_ops cmi_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - -static int patch_cm9825(struct hda_codec *codec) -{ - struct cmi_spec *spec; + struct hda_gen_spec *spec; struct auto_pin_cfg *cfg; + bool is_cmi8888 = id->vendor_id == 0x13f68888; int err; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; - INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed); codec->spec = spec; - spec->codec = codec; - codec->patch_ops = cmi_auto_patch_ops; - codec->patch_ops.init = cm9825_init; - codec->patch_ops.suspend = cm9825_suspend; - codec->patch_ops.resume = cm9825_resume; - codec->patch_ops.free = cm9825_free; - codec->patch_ops.check_power_status = snd_hda_gen_check_power_status; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - spec->chip_d0_verbs = cm9825_std_d0_verbs; - spec->chip_d3_verbs = cm9825_std_d3_verbs; - spec->chip_hp_present_verbs = cm9825_hp_present_verbs; - spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs; - - snd_hda_sequence_write(codec, spec->chip_d0_verbs); - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - goto error; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - goto error; - - cm9825_setup_unsol(codec); - - return 0; - - error: - cm9825_free(codec); - - codec_info(codec, "Enter err %d\n", err); - - return err; -} - -static int patch_cmi9880(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); + cfg = &spec->autocfg; + snd_hda_gen_spec_init(spec); + + if (is_cmi8888) { + /* mask NID 0x10 from the playback volume selection; + * it's a headphone boost volume handled manually below + */ + spec->out_vol_mask = (1ULL << 0x10); + } err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); if (err < 0) @@ -322,33 +46,6 @@ static int patch_cmi9880(struct hda_codec *codec) if (err < 0) goto error; - return 0; - - error: - snd_hda_gen_free(codec); - return err; -} - -static int patch_cmi8888(struct hda_codec *codec) -{ - struct cmi_spec *spec; - struct auto_pin_cfg *cfg; - int err; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - - codec->spec = spec; - codec->patch_ops = cmi_auto_patch_ops; - cfg = &spec->gen.autocfg; - snd_hda_gen_spec_init(&spec->gen); - - /* mask NID 0x10 from the playback volume selection; - * it's a headphone boost volume handled manually below - */ - spec->gen.out_vol_mask = (1ULL << 0x10); - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); if (err < 0) goto error; @@ -356,32 +53,44 @@ static int patch_cmi8888(struct hda_codec *codec) if (err < 0) goto error; - if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == - AC_JACK_HP_OUT) { - static const struct snd_kcontrol_new amp_kctl = - HDA_CODEC_VOLUME("Headphone Amp Playback Volume", - 0x10, 0, HDA_OUTPUT); - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &_kctl)) { - err = -ENOMEM; - goto error; + if (is_cmi8888) { + if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) == + AC_JACK_HP_OUT) { + static const struct snd_kcontrol_new amp_kctl = + HDA_CODEC_VOLUME("Headphone Amp Playback Volume", + 0x10, 0, HDA_OUTPUT); + if (!snd_hda_gen_add_kctl(spec, NULL, &_kctl)) { + err = -ENOMEM; + goto error; + } } } return 0; error: - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops cmedia_codec_ops = { + .probe = cmedia_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_cmedia[] = { - HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888), - HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880), - HDA_CODEC_ENTRY(0x13f69825, "CM9825", patch_cm9825), + HDA_CODEC_ID(0x13f68888, "CMI8888"), + HDA_CODEC_ID(0x13f69880, "CMI9880"), + HDA_CODEC_ID(0x434d4980, "CMI9880"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia); @@ -391,6 +100,7 @@ MODULE_DESCRIPTION("C-Media HD-audio codec"); static struct hda_codec_driver cmedia_driver = { .id = snd_hda_id_cmedia, + .ops = &cmedia_codec_ops, }; module_hda_codec_driver(cmedia_driver); -- GitLab From f025ef0316ce3650e8f4dc9de2709869c9217dac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:16 +0200 Subject: [PATCH 644/868] ALSA: hda/analog: Rewrite to new probe method Convert the Analog Device codec driver to use the new hda_codec_ops probe. The probe function had to be unified and branched with the model type specified via driver_data instead. Other than that, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-14-tiwai@suse.de --- sound/hda/codecs/analog.c | 221 +++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 110 deletions(-) diff --git a/sound/hda/codecs/analog.c b/sound/hda/codecs/analog.c index 3557e06c6d2b3..33aaeb44c4dce 100644 --- a/sound/hda/codecs/analog.c +++ b/sound/hda/codecs/analog.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, + * HD audio codec driver for AD1882, AD1884, AD1981HD, AD1983, AD1984, * AD1986A, AD1988 * * Copyright (c) 2005-2007 Takashi Iwai @@ -18,9 +18,18 @@ #include "hda_jack.h" #include "generic.h" +enum { + MODEL_AD1882, + MODEL_AD1884, + MODEL_AD1981, + MODEL_AD1983, + MODEL_AD1986A, + MODEL_AD1988, +}; struct ad198x_spec { struct hda_gen_spec gen; + int model; /* for auto parser */ int smux_paths[4]; @@ -111,7 +120,7 @@ static void ad198x_power_eapd(struct hda_codec *codec) } } -static int ad198x_suspend(struct hda_codec *codec) +static int ad_codec_suspend(struct hda_codec *codec) { snd_hda_shutup_pins(codec); ad198x_power_eapd(codec); @@ -137,7 +146,7 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled) * Automatic parse of I/O pins from the BIOS configuration */ -static int ad198x_auto_build_controls(struct hda_codec *codec) +static int ad_codec_build_controls(struct hda_codec *codec) { int err; @@ -150,17 +159,6 @@ static int ad198x_auto_build_controls(struct hda_codec *codec) return 0; } -static const struct hda_codec_ops ad198x_auto_patch_ops = { - .build_controls = ad198x_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, - .suspend = ad198x_suspend, -}; - - static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) { struct ad198x_spec *spec = codec->spec; @@ -198,7 +196,6 @@ static int alloc_ad_spec(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; snd_hda_gen_spec_init(&spec->gen); - codec->patch_ops = ad198x_auto_patch_ops; return 0; } @@ -375,10 +372,10 @@ static const struct hda_model_fixup ad1986a_fixup_models[] = { /* */ -static int patch_ad1986a(struct hda_codec *codec) +static int ad1986a_probe(struct hda_codec *codec) { int err; - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; static const hda_nid_t preferred_pairs[] = { 0x1a, 0x03, 0x1b, 0x03, @@ -388,11 +385,6 @@ static int patch_ad1986a(struct hda_codec *codec) 0 }; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - /* AD1986A has the inverted EAPD implementation */ codec->inv_eapd = 1; @@ -418,10 +410,8 @@ static int patch_ad1986a(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = ad198x_parse_auto_config(codec, false); - if (err < 0) { - snd_hda_gen_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -507,18 +497,13 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) return 0; } -static int patch_ad1983(struct hda_codec *codec) +static int ad1983_probe(struct hda_codec *codec) { static const hda_nid_t conn_0c[] = { 0x08 }; static const hda_nid_t conn_0d[] = { 0x09 }; - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x0e; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); @@ -529,15 +514,11 @@ static int patch_ad1983(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, false); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -597,16 +578,11 @@ static const struct hda_quirk ad1981_fixup_tbl[] = { {} }; -static int patch_ad1981(struct hda_codec *codec) +static int ad1981_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return -ENOMEM; - spec = codec->spec; - spec->gen.mixer_nid = 0x0e; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); @@ -616,18 +592,14 @@ static int patch_ad1981(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, false); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -776,7 +748,7 @@ static const struct snd_kcontrol_new ad1988_auto_smux_mixer = { .put = ad1988_auto_smux_enum_put, }; -static int ad1988_auto_init(struct hda_codec *codec) +static int ad_codec_init(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; int i, err; @@ -784,6 +756,8 @@ static int ad1988_auto_init(struct hda_codec *codec) err = snd_hda_gen_init(codec); if (err < 0) return err; + if (spec->model != MODEL_AD1988) + return 0; if (!spec->gen.autocfg.dig_outs) return 0; @@ -854,8 +828,6 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) return -ENOMEM; - codec->patch_ops.init = ad1988_auto_init; - return 0; } @@ -889,16 +861,11 @@ static const struct hda_model_fixup ad1988_fixup_models[] = { {} }; -static int patch_ad1988(struct hda_codec *codec) +static int ad1988_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; @@ -909,18 +876,14 @@ static int patch_ad1988(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } @@ -1069,16 +1032,11 @@ static const struct hda_quirk ad1884_fixup_tbl[] = { }; -static int patch_ad1884(struct hda_codec *codec) +static int ad1884_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; @@ -1089,18 +1047,14 @@ static int patch_ad1884(struct hda_codec *codec) err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1983_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - snd_hda_gen_free(codec); - return err; } /* @@ -1115,53 +1069,99 @@ static int patch_ad1884(struct hda_codec *codec) * port-G - rear clfe-out (6stack) */ -static int patch_ad1882(struct hda_codec *codec) +static int ad1882_probe(struct hda_codec *codec) { - struct ad198x_spec *spec; + struct ad198x_spec *spec = codec->spec; int err; - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - spec->gen.mixer_nid = 0x20; spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); err = ad198x_parse_auto_config(codec, true); if (err < 0) - goto error; + return err; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) - goto error; + return err; return 0; - - error: - snd_hda_gen_free(codec); - return err; } - /* - * patch entries + * driver entries */ +static int ad_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) +{ + struct ad198x_spec *spec; + int err; + + err = alloc_ad_spec(codec); + if (err < 0) + return -ENOMEM; + spec = codec->spec; + spec->model = id->driver_data; + + switch (spec->model) { + case MODEL_AD1882: + err = ad1882_probe(codec); + break; + case MODEL_AD1884: + err = ad1884_probe(codec); + break; + case MODEL_AD1981: + err = ad1981_probe(codec); + break; + case MODEL_AD1983: + err = ad1983_probe(codec); + break; + case MODEL_AD1986A: + err = ad1986a_probe(codec); + break; + case MODEL_AD1988: + err = ad1988_probe(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_gen_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops ad_codec_ops = { + .probe = ad_codec_probe, + .remove = snd_hda_gen_remove, + .build_controls = ad_codec_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = ad_codec_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = ad_codec_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_analog[] = { - HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882), - HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884), - HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981), - HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983), - HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884), - HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a), - HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882), - HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988), - HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988), + HDA_CODEC_ID_MODEL(0x11d4184a, "AD1884A", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41882, "AD1882", MODEL_AD1882), + HDA_CODEC_ID_MODEL(0x11d41883, "AD1883", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41884, "AD1884", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d4194a, "AD1984A", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d4194b, "AD1984B", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41981, "AD1981", MODEL_AD1981), + HDA_CODEC_ID_MODEL(0x11d41983, "AD1983", MODEL_AD1983), + HDA_CODEC_ID_MODEL(0x11d41984, "AD1984", MODEL_AD1884), + HDA_CODEC_ID_MODEL(0x11d41986, "AD1986A", MODEL_AD1986A), + HDA_CODEC_ID_MODEL(0x11d41988, "AD1988", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4198b, "AD1988B", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4882a, "AD1882A", MODEL_AD1882), + HDA_CODEC_ID_MODEL(0x11d4989a, "AD1989A", MODEL_AD1988), + HDA_CODEC_ID_MODEL(0x11d4989b, "AD1989B", MODEL_AD1988), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); @@ -1171,6 +1171,7 @@ MODULE_DESCRIPTION("Analog Devices HD-audio codec"); static struct hda_codec_driver analog_driver = { .id = snd_hda_id_analog, + .ops = &ad_codec_ops, }; module_hda_codec_driver(analog_driver); -- GitLab From dbe3e4ab57dd92b1f6bd9ba39aff43aa7a53749a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:17 +0200 Subject: [PATCH 645/868] ALSA: hda/ca0110: Rewrite to new probe method Convert the CA0110 codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-15-tiwai@suse.de --- sound/hda/codecs/ca0110.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/sound/hda/codecs/ca0110.c b/sound/hda/codecs/ca0110.c index 0353544435b9a..c75a9ff9460dd 100644 --- a/sound/hda/codecs/ca0110.c +++ b/sound/hda/codecs/ca0110.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Creative X-Fi CA0110-IBG chip + * HD audio codec driver for Creative X-Fi CA0110-IBG chip * * Copyright (c) 2008 Takashi Iwai */ @@ -15,15 +15,6 @@ #include "hda_jack.h" #include "generic.h" - -static const struct hda_codec_ops ca0110_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - static int ca0110_parse_auto_config(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -39,8 +30,7 @@ static int ca0110_parse_auto_config(struct hda_codec *codec) return 0; } - -static int patch_ca0110(struct hda_codec *codec) +static int ca0110_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct hda_gen_spec *spec; int err; @@ -50,7 +40,6 @@ static int patch_ca0110(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(spec); codec->spec = spec; - codec->patch_ops = ca0110_patch_ops; spec->multi_cap_vol = 1; codec->bus->core.needs_damn_long_delay = 1; @@ -62,18 +51,27 @@ static int patch_ca0110(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); return err; } +static const struct hda_codec_ops ca0110_codec_ops = { + .probe = ca0110_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .unsol_event = snd_hda_jack_unsol_event, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_ca0110[] = { - HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110), - HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110), + HDA_CODEC_ID(0x1102000a, "CA0110-IBG"), + HDA_CODEC_ID(0x1102000b, "CA0110-IBG"), + HDA_CODEC_ID(0x1102000d, "SB0880 X-Fi"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110); @@ -83,6 +81,7 @@ MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); static struct hda_codec_driver ca0110_driver = { .id = snd_hda_id_ca0110, + .ops = &ca0110_codec_ops, }; module_hda_codec_driver(ca0110_driver); -- GitLab From 1cb8744a36c7e8faa7098207f835af22a65717b5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:18 +0200 Subject: [PATCH 646/868] ALSA: hda/cirrus: Split to cs420x and cs421x drivers Since the codec ops for CS420x and CS421x are fairly independent, split the cirrus codec driver into two drivers, snd-hda-codec-cs420x and snd-hda-code-cs421x. Together with the split, convert to the new hda_codec_ops probe. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-16-tiwai@suse.de --- sound/hda/codecs/cirrus/Kconfig | 20 +- sound/hda/codecs/cirrus/Makefile | 6 +- .../hda/codecs/cirrus/{cirrus.c => cs420x.c} | 526 +--------------- sound/hda/codecs/cirrus/cs421x.c | 590 ++++++++++++++++++ 4 files changed, 643 insertions(+), 499 deletions(-) rename sound/hda/codecs/cirrus/{cirrus.c => cs420x.c} (61%) create mode 100644 sound/hda/codecs/cirrus/cs421x.c diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig index f6cefb65c5f87..b3a5968e9a025 100644 --- a/sound/hda/codecs/cirrus/Kconfig +++ b/sound/hda/codecs/cirrus/Kconfig @@ -1,14 +1,24 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_HDA_CODEC_CIRRUS - tristate "Build Cirrus Logic codec support" +config SND_HDA_CODEC_CS420X + tristate "Build Cirrus Logic CS420x codec support" select SND_HDA_GENERIC help - Say Y or M here to include Cirrus Logic codec support in - snd-hda-intel driver, such as CS4206. + Say Y or M here to include Cirrus Logic CS420x codec support in + snd-hda-intel driver comment "Set to Y if you want auto-loading the codec driver" - depends on SND_HDA=y && SND_HDA_CODEC_CIRRUS=m + depends on SND_HDA=y && SND_HDA_CODEC_CS420X=m + +config SND_HDA_CODEC_CS421X + tristate "Build Cirrus Logic CS421x codec support" + select SND_HDA_GENERIC + help + Say Y or M here to include Cirrus Logic CS421x codec support in + snd-hda-intel driver + +comment "Set to Y if you want auto-loading the codec driver" + depends on SND_HDA=y && SND_HDA_CODEC_CS421X=m config SND_HDA_CODEC_CS8409 tristate "Build Cirrus Logic HDA bridge support" diff --git a/sound/hda/codecs/cirrus/Makefile b/sound/hda/codecs/cirrus/Makefile index fa40c893fb09a..dda1873ebcf5c 100644 --- a/sound/hda/codecs/cirrus/Makefile +++ b/sound/hda/codecs/cirrus/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 subdir-ccflags-y += -I$(src)/../../common -snd-hda-codec-cirrus-y := cirrus.o +snd-hda-codec-cs420x-y := cs420x.o +snd-hda-codec-cs421x-y := cs421x.o snd-hda-codec-cs8409-y := cs8409.o cs8409-tables.o -obj-$(CONFIG_SND_HDA_CODEC_CIRRUS) += snd-hda-codec-cirrus.o +obj-$(CONFIG_SND_HDA_CODEC_CS420X) += snd-hda-codec-cs420x.o +obj-$(CONFIG_SND_HDA_CODEC_CS421X) += snd-hda-codec-cs421x.o obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o diff --git a/sound/hda/codecs/cirrus/cirrus.c b/sound/hda/codecs/cirrus/cs420x.c similarity index 61% rename from sound/hda/codecs/cirrus/cirrus.c rename to sound/hda/codecs/cirrus/cs420x.c index 81ea66c4e9d31..823220d5cadac 100644 --- a/sound/hda/codecs/cirrus/cirrus.c +++ b/sound/hda/codecs/cirrus/cs420x.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Cirrus Logic CS420x chip + * Cirrus Logic CS420x HD-audio codec * * Copyright (c) 2009 Takashi Iwai */ @@ -17,9 +17,6 @@ #include "hda_jack.h" #include "../generic.h" -/* - */ - struct cs_spec { struct hda_gen_spec gen; @@ -29,10 +26,6 @@ struct cs_spec { unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ - /* CS421x */ - unsigned int spdif_detect:1; - unsigned int spdif_present:1; - unsigned int sense_b:1; hda_nid_t vendor_nid; /* for MBP SPDIF control */ @@ -56,13 +49,6 @@ enum { CS420X_APPLE = CS420X_GPIO_13, }; -/* CS421x boards */ -enum { - CS421X_CDB4210, - CS421X_SENSE_B, - CS421X_STUMPY, -}; - /* Vendor-specific processing widget */ #define CS420X_VENDOR_NID 0x11 #define CS_DIG_OUT1_PIN_NID 0x10 @@ -105,28 +91,6 @@ enum { /* Cirrus Logic CS4208 */ #define CS4208_VENDOR_NID 0x24 -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ -#define CS4210_DAC_NID 0x02 -#define CS4210_ADC_NID 0x03 -#define CS4210_VENDOR_NID 0x0B -#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ -#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ - -#define CS421X_IDX_DEV_CFG 0x01 -#define CS421X_IDX_ADC_CFG 0x02 -#define CS421X_IDX_DAC_CFG 0x03 -#define CS421X_IDX_SPK_CTL 0x04 - -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 - - static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) { struct cs_spec *spec = codec->spec; @@ -158,9 +122,6 @@ static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - /* mute HPs if spdif jack (SENSE_B) is present */ - spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); - snd_hda_gen_update_outputs(codec); if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { @@ -331,16 +292,6 @@ static int cs_build_controls(struct hda_codec *codec) return 0; } -#define cs_free snd_hda_gen_free - -static const struct hda_codec_ops cs_patch_ops = { - .build_controls = cs_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, -}; - static int cs_parse_auto_config(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; @@ -584,17 +535,10 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) return spec; } -static int patch_cs420x(struct hda_codec *codec) +static int cs420x_probe(struct hda_codec *codec) { - struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; codec->single_adc_amp = 1; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, @@ -603,15 +547,11 @@ static int patch_cs420x(struct hda_codec *codec) err = cs_parse_auto_config(codec); if (err < 0) - goto error; + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; - - error: - cs_free(codec); - return err; } /* @@ -766,17 +706,11 @@ static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc) snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps); } -static int patch_cs4208(struct hda_codec *codec) +static int cs4208_probe(struct hda_codec *codec) { - struct cs_spec *spec; + struct cs_spec *spec = codec->spec; int err; - spec = cs_alloc_spec(codec, CS4208_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs_patch_ops; - spec->gen.automute_hook = cs_automute; /* exclude NID 0x10 (HP) from output volumes due to different steps */ spec->gen.out_vol_mask = 1ULL << 0x10; @@ -791,453 +725,61 @@ static int patch_cs4208(struct hda_codec *codec) cs4208_fix_amp_caps(codec, 0x1c); err = cs_parse_auto_config(codec); - if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); - return err; -} - -/* - * Cirrus Logic CS4210 - * - * 1 DAC => HP(sense) / Speakers, - * 1 ADC <= LineIn(sense) / MicIn / DMicIn, - * 1 SPDIF OUT => SPDIF Trasmitter(sense) - */ - -/* CS4210 board names */ -static const struct hda_model_fixup cs421x_models[] = { - { .id = CS421X_CDB4210, .name = "cdb4210" }, - { .id = CS421X_STUMPY, .name = "stumpy" }, - {} -}; - -static const struct hda_quirk cs421x_fixup_tbl[] = { - /* Test Intel board + CDB2410 */ - SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), - {} /* terminator */ -}; - -/* CS4210 board pinconfigs */ -/* Default CS4210 (CDB4210)*/ -static const struct hda_pintbl cdb4210_pincfgs[] = { - { 0x05, 0x0321401f }, - { 0x06, 0x90170010 }, - { 0x07, 0x03813031 }, - { 0x08, 0xb7a70037 }, - { 0x09, 0xb7a6003e }, - { 0x0a, 0x034510f0 }, - {} /* terminator */ -}; - -/* Stumpy ChromeBox */ -static const struct hda_pintbl stumpy_pincfgs[] = { - { 0x05, 0x022120f0 }, - { 0x06, 0x901700f0 }, - { 0x07, 0x02a120f0 }, - { 0x08, 0x77a70037 }, - { 0x09, 0x77a6003e }, - { 0x0a, 0x434510f0 }, - {} /* terminator */ -}; - -/* Setup GPIO/SENSE for each board (if used) */ -static void cs421x_fixup_sense_b(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct cs_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->sense_b = 1; -} - -static const struct hda_fixup cs421x_fixups[] = { - [CS421X_CDB4210] = { - .type = HDA_FIXUP_PINS, - .v.pins = cdb4210_pincfgs, - .chained = true, - .chain_id = CS421X_SENSE_B, - }, - [CS421X_SENSE_B] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs421x_fixup_sense_b, - }, - [CS421X_STUMPY] = { - .type = HDA_FIXUP_PINS, - .v.pins = stumpy_pincfgs, - }, -}; - -static const struct hda_verb cs421x_coef_init_verbs[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 1}, - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, - /* - * Disable Coefficient Index Auto-Increment(DAI)=1, - * PDREF=0 - */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, - /* ADC SZCMode = Digital Soft Ramp */ - {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, - - {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, - {0x0B, AC_VERB_SET_PROC_COEF, - (0x0002 /* DAC SZCMode = Digital Soft Ramp */ - | 0x0004 /* Mute DAC on FIFO error */ - | 0x0008 /* Enable DAC High Pass Filter */ - )}, - {} /* terminator */ -}; - -/* Errata: CS4210 rev A1 Silicon - * - * http://www.cirrus.com/en/pubs/errata/ - * - * Description: - * 1. Performance degredation is present in the ADC. - * 2. Speaker output is not completely muted upon HP detect. - * 3. Noise is present when clipping occurs on the amplified - * speaker outputs. - * - * Workaround: - * The following verb sequence written to the registers during - * initialization will correct the issues listed above. - */ - -static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { - {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, - {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, - {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ - - {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, - {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ - - {} /* terminator */ -}; - -/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ -static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); - -static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - return 0; -} - -static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; - return 0; -} - -static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - - unsigned int vol = ucontrol->value.integer.value[0]; - unsigned int coef = - cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); - unsigned int original_coef = coef; - - coef &= ~0x0003; - coef |= (vol & 0x0003); - if (original_coef != coef) { - cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); - return 1; - } - - return 0; -} - -static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { - - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ), - .name = "Speaker Boost Playback Volume", - .info = cs421x_boost_vol_info, - .get = cs421x_boost_vol_get, - .put = cs421x_boost_vol_put, - .tlv = { .p = cs421x_speaker_boost_db_scale }, -}; - -static void cs4210_pinmux_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int def_conf, coef; - - /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - - if (spec->gpio_mask) - coef |= 0x0008; /* B1,B2 are GPIOs */ - else - coef &= ~0x0008; - - if (spec->sense_b) - coef |= 0x0010; /* B2 is SENSE_B, not inverted */ - else - coef &= ~0x0010; - - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - - if ((spec->gpio_mask || spec->sense_b) && - is_active_pin(codec, CS421X_DMIC_PIN_NID)) { - - /* - * GPIO or SENSE_B forced - disconnect the DMIC pin. - */ - def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); - def_conf &= ~AC_DEFCFG_PORT_CONN; - def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); - snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); - } -} - -static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_callback *tbl) -{ - struct cs_spec *spec = codec->spec; - bool spdif_present = false; - hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; - - /* detect on spdif is specific to CS4210 */ - if (!spec->spdif_detect || - spec->vendor_nid != CS4210_VENDOR_NID) - return; - - spdif_present = snd_hda_jack_detect(codec, spdif_pin); - if (spdif_present == spec->spdif_present) - return; - - spec->spdif_present = spdif_present; - /* SPDIF TX on/off */ - snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0); - - cs_automute(codec); -} - -static void parse_cs421x_digital(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int i; - - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t nid = cfg->dig_out_pins[i]; - - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { - spec->spdif_detect = 1; - snd_hda_jack_detect_enable_callback(codec, nid, - cs4210_spdif_automute); - } - } -} - -static int cs421x_init(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - snd_hda_sequence_write(codec, cs421x_coef_init_verbs); - snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); - cs4210_pinmux_init(codec); - } - - snd_hda_gen_init(codec); - - if (spec->gpio_mask) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, - spec->gpio_mask); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, - spec->gpio_dir); - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_data); - } - - init_input_coef(codec); - - cs4210_spdif_automute(codec, NULL); - - return 0; -} - -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) -{ - unsigned int caps; - - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); -} - -static int cs421x_parse_auto_config(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - hda_nid_t dac = CS4210_DAC_NID; - int err; - - fix_volume_caps(codec, dac); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); if (err < 0) return err; - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - parse_cs421x_digital(codec); - - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, - &cs421x_speaker_boost_ctl)) - return -ENOMEM; - } - - return 0; -} - -/* - * Manage PDREF, when transitioning to D3hot - * (DAC,ADC) -> D3, PDREF=1, AFG->D3 - */ -static int cs421x_suspend(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - unsigned int coef; - - snd_hda_shutup_pins(codec); - - snd_hda_codec_write(codec, CS4210_DAC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - snd_hda_codec_write(codec, CS4210_ADC_NID, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - - if (spec->vendor_nid == CS4210_VENDOR_NID) { - coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); - coef |= 0x0004; /* PDREF */ - cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); - } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -static const struct hda_codec_ops cs421x_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs421x_init, - .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cs421x_suspend, -}; - -static int patch_cs4210(struct hda_codec *codec) +static int cs_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); + spec = cs_alloc_spec(codec, id->driver_data); if (!spec) return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; spec->gen.automute_hook = cs_automute; - snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, - cs421x_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - /* - * Update the GPIO/DMIC/SENSE_B pinmux before the configuration - * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input - * is disabled. - */ - cs4210_pinmux_init(codec); - - err = cs421x_parse_auto_config(codec); + if (spec->vendor_nid == CS4208_VENDOR_NID) + err = cs4208_probe(codec); + else + err = cs420x_probe(codec); if (err < 0) - goto error; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - - return 0; - - error: - cs_free(codec); + snd_hda_gen_remove(codec); return err; } -static int patch_cs4213(struct hda_codec *codec) -{ - struct cs_spec *spec; - int err; - - spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); - if (!spec) - return -ENOMEM; - - codec->patch_ops = cs421x_patch_ops; - - err = cs421x_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - cs_free(codec); - return err; -} +static const struct hda_codec_ops cs_codec_ops = { + .probe = cs_codec_probe, + .remove = snd_hda_gen_remove, + .build_controls = cs_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs_init, + .unsol_event = snd_hda_jack_unsol_event, + .stream_pm = snd_hda_gen_stream_pm, +}; /* - * patch entries + * driver entries */ -static const struct hda_device_id snd_hda_id_cirrus[] = { - HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x), - HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x), - HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208), - HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210), - HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213), +static const struct hda_device_id snd_hda_id_cs420x[] = { + HDA_CODEC_ID_MODEL(0x10134206, "CS4206", CS420X_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134207, "CS4207", CS420X_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134208, "CS4208", CS4208_VENDOR_NID), {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs420x); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); +MODULE_DESCRIPTION("Cirrus Logic CS420x HD-audio codec"); -static struct hda_codec_driver cirrus_driver = { - .id = snd_hda_id_cirrus, +static struct hda_codec_driver cs420x_driver = { + .id = snd_hda_id_cs420x, + .ops = &cs_codec_ops, }; -module_hda_codec_driver(cirrus_driver); +module_hda_codec_driver(cs420x_driver); diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c new file mode 100644 index 0000000000000..a93e2e0bb391c --- /dev/null +++ b/sound/hda/codecs/cirrus/cs421x.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cirrus Logic CS421x HD-audio codec + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "../generic.h" + +struct cs_spec { + struct hda_gen_spec gen; + + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ + unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int spdif_present:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + + /* for MBP SPDIF control */ + int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +}; + +/* CS421x boards */ +enum { + CS421X_CDB4210, + CS421X_SENSE_B, + CS421X_STUMPY, +}; + +/* Vendor-specific processing widget */ +#define CS_DIG_OUT1_PIN_NID 0x10 +#define CS_DIG_OUT2_PIN_NID 0x15 +#define CS_DMIC1_PIN_NID 0x0e +#define CS_DMIC2_PIN_NID 0x12 + +/* coef indices */ +#define IDX_SPDIF_STAT 0x0000 +#define IDX_SPDIF_CTL 0x0001 +#define IDX_ADC_CFG 0x0002 +/* SZC bitmask, 4 modes below: + * 0 = immediate, + * 1 = digital immediate, analog zero-cross + * 2 = digtail & analog soft-ramp + * 3 = digital soft-ramp, analog zero-cross + */ +#define CS_COEF_ADC_SZC_MASK (3 << 0) +#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */ +#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */ +/* PGA mode: 0 = differential, 1 = signle-ended */ +#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */ +#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */ +#define IDX_DAC_CFG 0x0003 +/* SZC bitmask, 4 modes below: + * 0 = Immediate + * 1 = zero-cross + * 2 = soft-ramp + * 3 = soft-ramp on zero-cross + */ +#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */ +#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */ +#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */ + +#define IDX_BEEP_CFG 0x0004 +/* 0x0008 - test reg key */ +/* 0x0009 - 0x0014 -> 12 test regs */ +/* 0x0015 - visibility reg */ + +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Transmitter(sense) + */ +#define CS4210_DAC_NID 0x02 +#define CS4210_ADC_NID 0x03 +#define CS4210_VENDOR_NID 0x0B +#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ +#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ + +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 + +/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ +#define CS4213_VENDOR_NID 0x09 + + +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + return snd_hda_codec_read(codec, spec->vendor_nid, 0, + AC_VERB_GET_PROC_COEF, 0); +} + +static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, + unsigned int coef) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_COEF_INDEX, idx); + snd_hda_codec_write(codec, spec->vendor_nid, 0, + AC_VERB_SET_PROC_COEF, coef); +} + +/* + * auto-mute and auto-mic switching + * CS421x auto-output redirecting + * HP/SPK/SPDIF + */ + +static void cs_automute(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + /* mute HPs if spdif jack (SENSE_B) is present */ + spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); + + snd_hda_gen_update_outputs(codec); + + if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { + if (spec->gen.automute_speaker) + spec->gpio_data = spec->gen.hp_jack_present ? + spec->gpio_eapd_hp : spec->gpio_eapd_speaker; + else + spec->gpio_data = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, spec->gpio_data); + } +} + +static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); +} + +static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) +{ + struct cs_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return NULL; + codec->spec = spec; + spec->vendor_nid = vendor_nid; + codec->power_save_node = 1; + snd_hda_gen_spec_init(&spec->gen); + + return spec; +} + +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Transmitter(sense) + */ + +/* CS4210 board names */ +static const struct hda_model_fixup cs421x_models[] = { + { .id = CS421X_CDB4210, .name = "cdb4210" }, + { .id = CS421X_STUMPY, .name = "stumpy" }, + {} +}; + +static const struct hda_quirk cs421x_fixup_tbl[] = { + /* Test Intel board + CDB2410 */ + SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), + {} /* terminator */ +}; + +/* CS4210 board pinconfigs */ +/* Default CS4210 (CDB4210)*/ +static const struct hda_pintbl cdb4210_pincfgs[] = { + { 0x05, 0x0321401f }, + { 0x06, 0x90170010 }, + { 0x07, 0x03813031 }, + { 0x08, 0xb7a70037 }, + { 0x09, 0xb7a6003e }, + { 0x0a, 0x034510f0 }, + {} /* terminator */ +}; + +/* Stumpy ChromeBox */ +static const struct hda_pintbl stumpy_pincfgs[] = { + { 0x05, 0x022120f0 }, + { 0x06, 0x901700f0 }, + { 0x07, 0x02a120f0 }, + { 0x08, 0x77a70037 }, + { 0x09, 0x77a6003e }, + { 0x0a, 0x434510f0 }, + {} /* terminator */ +}; + +/* Setup GPIO/SENSE for each board (if used) */ +static void cs421x_fixup_sense_b(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct cs_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->sense_b = 1; +} + +static const struct hda_fixup cs421x_fixups[] = { + [CS421X_CDB4210] = { + .type = HDA_FIXUP_PINS, + .v.pins = cdb4210_pincfgs, + .chained = true, + .chain_id = CS421X_SENSE_B, + }, + [CS421X_SENSE_B] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs421x_fixup_sense_b, + }, + [CS421X_STUMPY] = { + .type = HDA_FIXUP_PINS, + .v.pins = stumpy_pincfgs, + }, +}; + +static const struct hda_verb cs421x_coef_init_verbs[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 1}, + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, + /* + * Disable Coefficient Index Auto-Increment(DAI)=1, + * PDREF=0 + */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, + /* ADC SZCMode = Digital Soft Ramp */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, + {0x0B, AC_VERB_SET_PROC_COEF, + (0x0002 /* DAC SZCMode = Digital Soft Ramp */ + | 0x0004 /* Mute DAC on FIFO error */ + | 0x0008 /* Enable DAC High Pass Filter */ + )}, + {} /* terminator */ +}; + +/* Errata: CS4210 rev A1 Silicon + * + * http://www.cirrus.com/en/pubs/errata/ + * + * Description: + * 1. Performance degredation is present in the ADC. + * 2. Speaker output is not completely muted upon HP detect. + * 3. Noise is present when clipping occurs on the amplified + * speaker outputs. + * + * Workaround: + * The following verb sequence written to the registers during + * initialization will correct the issues listed above. + */ + +static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, + {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, + {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ + + {} /* terminator */ +}; + +/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ +static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); + +static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; + return 0; +} + +static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + unsigned int vol = ucontrol->value.integer.value[0]; + unsigned int coef = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); + unsigned int original_coef = coef; + + coef &= ~0x0003; + coef |= (vol & 0x0003); + if (original_coef != coef) { + cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Speaker Boost Playback Volume", + .info = cs421x_boost_vol_info, + .get = cs421x_boost_vol_get, + .put = cs421x_boost_vol_put, + .tlv = { .p = cs421x_speaker_boost_db_scale }, +}; + +static void cs4210_pinmux_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int def_conf, coef; + + /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + + if (spec->gpio_mask) + coef |= 0x0008; /* B1,B2 are GPIOs */ + else + coef &= ~0x0008; + + if (spec->sense_b) + coef |= 0x0010; /* B2 is SENSE_B, not inverted */ + else + coef &= ~0x0010; + + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + if ((spec->gpio_mask || spec->sense_b) && + is_active_pin(codec, CS421X_DMIC_PIN_NID)) { + + /* + * GPIO or SENSE_B forced - disconnect the DMIC pin. + */ + def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); + def_conf &= ~AC_DEFCFG_PORT_CONN; + def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); + snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); + } +} + +static void cs4210_spdif_automute(struct hda_codec *codec, + struct hda_jack_callback *tbl) +{ + struct cs_spec *spec = codec->spec; + bool spdif_present = false; + hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; + + /* detect on spdif is specific to CS4210 */ + if (!spec->spdif_detect || + spec->vendor_nid != CS4210_VENDOR_NID) + return; + + spdif_present = snd_hda_jack_detect(codec, spdif_pin); + if (spdif_present == spec->spdif_present) + return; + + spec->spdif_present = spdif_present; + /* SPDIF TX on/off */ + snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0); + + cs_automute(codec); +} + +static void parse_cs421x_digital(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + int i; + + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t nid = cfg->dig_out_pins[i]; + + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + spec->spdif_detect = 1; + snd_hda_jack_detect_enable_callback(codec, nid, + cs4210_spdif_automute); + } + } +} + +static int cs421x_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + snd_hda_sequence_write(codec, cs421x_coef_init_verbs); + snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); + cs4210_pinmux_init(codec); + } + + snd_hda_gen_init(codec); + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + cs4210_spdif_automute(codec, NULL); + + return 0; +} + +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +{ + unsigned int caps; + + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); +} + +static int cs421x_parse_auto_config(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + hda_nid_t dac = CS4210_DAC_NID; + int err; + + fix_volume_caps(codec, dac); + + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + if (err < 0) + return err; + + parse_cs421x_digital(codec); + + if (spec->gen.autocfg.speaker_outs && + spec->vendor_nid == CS4210_VENDOR_NID) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &cs421x_speaker_boost_ctl)) + return -ENOMEM; + } + + return 0; +} + +/* + * Manage PDREF, when transitioning to D3hot + * (DAC,ADC) -> D3, PDREF=1, AFG->D3 + */ +static int cs421x_suspend(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int coef; + + snd_hda_shutup_pins(codec); + + snd_hda_codec_write(codec, CS4210_DAC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, CS4210_ADC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + coef |= 0x0004; /* PDREF */ + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + } + + return 0; +} + +static int cs421x_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct cs_spec *spec; + int err; + + spec = cs_alloc_spec(codec, id->driver_data); + if (!spec) + return -ENOMEM; + + spec->gen.automute_hook = cs_automute; + + if (spec->vendor_nid == CS4210_VENDOR_NID) { + snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, + cs421x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + /* + * Update the GPIO/DMIC/SENSE_B pinmux before the configuration + * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input + * is disabled. + */ + cs4210_pinmux_init(codec); + } + + err = cs421x_parse_auto_config(codec); + if (err < 0) + goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + + return 0; + + error: + snd_hda_gen_remove(codec); + return err; +} + +static const struct hda_codec_ops cs421x_codec_ops = { + .probe = cs421x_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs421x_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cs421x_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; + +/* + * driver entries + */ +static const struct hda_device_id snd_hda_id_cs421x[] = { + HDA_CODEC_ID_MODEL(0x10134210, "CS4210", CS4210_VENDOR_NID), + HDA_CODEC_ID_MODEL(0x10134213, "CS4213", CS4213_VENDOR_NID), + {} /* terminator */ +}; +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs421x); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cirrus Logic CS421x HD-audio codec"); + +static struct hda_codec_driver cs421x_driver = { + .id = snd_hda_id_cs421x, + .ops = &cs421x_codec_ops, +}; + +module_hda_codec_driver(cs421x_driver); -- GitLab From e4c9f524a12c2e5fbd3dc1fb4c0bdadbe4d89f6f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:19 +0200 Subject: [PATCH 647/868] ALSA: hda/cs8409: Rewrite to new probe method Convert the CS8409 codec driver to use the new hda_codec_ops probe. The Dolphin support needs an override of unsol_event callback, and redirect via spec->unsol_event function pointer for now. Other than that, no functional changes. Reviewed-by: Richard Fitzgerald Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-17-tiwai@suse.de --- sound/hda/codecs/cirrus/cs8409-tables.c | 2 +- sound/hda/codecs/cirrus/cs8409.c | 55 +++++++++++++------------ sound/hda/codecs/cirrus/cs8409.h | 4 +- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c index 5fe49f13c0da5..8c703b714a71d 100644 --- a/sound/hda/codecs/cirrus/cs8409-tables.c +++ b/sound/hda/codecs/cirrus/cs8409-tables.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * cs8409-tables.c -- HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * cs8409-tables.c -- HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c index 5ec1126b2a55a..e32b462cdc5e3 100644 --- a/sound/hda/codecs/cirrus/cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -954,7 +954,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data); } -static void cs8409_free(struct hda_codec *codec) +static void cs8409_remove(struct hda_codec *codec) { struct cs8409_spec *spec = codec->spec; @@ -962,7 +962,7 @@ static void cs8409_free(struct hda_codec *codec) cancel_delayed_work_sync(&spec->i2c_clk_work); cs8409_disable_i2c_clock(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } /****************************************************************************** @@ -1007,6 +1007,16 @@ static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned in } } +static void cs8409_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs8409_spec *spec = codec->spec; + + if (spec->unsol_event) + spec->unsol_event(codec, res); + else + cs8409_cs42l42_jack_unsol_event(codec, res); +} + /* Manage PDREF, when transition to D3hot */ static int cs8409_cs42l42_suspend(struct hda_codec *codec) { @@ -1076,15 +1086,6 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec) cs8409_enable_ur(codec, 1); } -static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = cs8409_cs42l42_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res) { @@ -1134,7 +1135,6 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec; spec->num_scodecs = 1; spec->scodecs[CS8409_CODEC0]->codec = codec; - codec->patch_ops = cs8409_cs42l42_patch_ops; spec->gen.suppress_auto_mute = 1; spec->gen.no_primary_hp = 1; @@ -1304,15 +1304,6 @@ static void dolphin_hw_init(struct hda_codec *codec) cs8409_enable_ur(codec, 1); } -static const struct hda_codec_ops cs8409_dolphin_patch_ops = { - .build_controls = cs8409_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cs8409_init, - .free = cs8409_free, - .unsol_event = dolphin_jack_unsol_event, - .suspend = cs8409_cs42l42_suspend, -}; - static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res) { @@ -1371,7 +1362,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac spec->num_scodecs = 2; spec->gen.suppress_vmaster = 1; - codec->patch_ops = cs8409_dolphin_patch_ops; + spec->unsol_event = dolphin_jack_unsol_event; /* GPIO 1,5 out, 0,4 in */ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio | @@ -1444,7 +1435,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac } } -static int patch_cs8409(struct hda_codec *codec) +static int cs8409_probe(struct hda_codec *codec, const struct hda_device_id *id) { int err; @@ -1461,7 +1452,7 @@ static int patch_cs8409(struct hda_codec *codec) err = cs8409_parse_auto_config(codec); if (err < 0) { - cs8409_free(codec); + cs8409_remove(codec); return err; } @@ -1469,14 +1460,26 @@ static int patch_cs8409(struct hda_codec *codec) return 0; } +static const struct hda_codec_ops cs8409_codec_ops = { + .probe = cs8409_probe, + .remove = cs8409_remove, + .build_controls = cs8409_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cs8409_init, + .unsol_event = cs8409_unsol_event, + .suspend = cs8409_cs42l42_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; + static const struct hda_device_id snd_hda_id_cs8409[] = { - HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409), + HDA_CODEC_ID(0x10138409, "CS8409"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409); static struct hda_codec_driver cs8409_driver = { .id = snd_hda_id_cs8409, + .ops = &cs8409_codec_ops, }; module_hda_codec_driver(cs8409_driver); diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h index 35072cd009dc2..7fe56f4a73bcb 100644 --- a/sound/hda/codecs/cirrus/cs8409.h +++ b/sound/hda/codecs/cirrus/cs8409.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * HD audio interface patch for Cirrus Logic CS8409 HDA bridge chip + * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip * * Copyright (C) 2021 Cirrus Logic, Inc. and * Cirrus Logic International Semiconductor Ltd. @@ -345,6 +345,8 @@ struct cs8409_spec { /* verb exec op override */ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res); + /* unsol_event op override */ + void (*unsol_event)(struct hda_codec *codec, unsigned int res); }; extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; -- GitLab From dec96130eae338fc04756df52d8492713755610d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:20 +0200 Subject: [PATCH 648/868] ALSA: hda/conexant: Rewrite to new probe method Convert the Conexant codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-18-tiwai@suse.de --- sound/hda/codecs/conexant.c | 104 ++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index b7710a81fd87b..c881bf213ebe6 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Conexant HDA audio codec + * HD audio codec driver for Conexant HDA audio codec * * Copyright (c) 2006 Pototskiy Akex * Takashi Iwai @@ -185,7 +185,7 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20); } -static int cx_auto_init(struct hda_codec *codec) +static int cx_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; snd_hda_gen_init(codec); @@ -210,10 +210,10 @@ static void cx_auto_shutdown(struct hda_codec *codec) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); } -static void cx_auto_free(struct hda_codec *codec) +static void cx_remove(struct hda_codec *codec) { cx_auto_shutdown(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } static void cx_process_headset_plugin(struct hda_codec *codec) @@ -258,22 +258,12 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_ cx_process_headset_plugin(codec); } -static int cx_auto_suspend(struct hda_codec *codec) +static int cx_suspend(struct hda_codec *codec) { cx_auto_shutdown(codec); return 0; } -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = cx_auto_init, - .free = cx_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = cx_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - /* * pin fix-up */ @@ -1178,7 +1168,7 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec) spec->gen.dac_min_mute = true; } -static int patch_conexant_auto(struct hda_codec *codec) +static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct conexant_spec *spec; int err; @@ -1190,7 +1180,6 @@ static int patch_conexant_auto(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - codec->patch_ops = cx_auto_patch_ops; /* init cx11880/sn6140 flag and reset headset_present_flag */ switch (codec->core.vendor_id) { @@ -1276,47 +1265,59 @@ static int patch_conexant_auto(struct hda_codec *codec) return 0; error: - cx_auto_free(codec); + cx_remove(codec); return err; } +static const struct hda_codec_ops cx_codec_ops = { + .probe = cx_probe, + .remove = cx_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = cx_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = cx_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* */ static const struct hda_device_id snd_hda_id_conexant[] = { - HDA_CODEC_ENTRY(0x14f11f86, "CX11880", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f11f87, "SN6140", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto), - HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto), + HDA_CODEC_ID(0x14f11f86, "CX11880"), + HDA_CODEC_ID(0x14f11f87, "SN6140"), + HDA_CODEC_ID(0x14f12008, "CX8200"), + HDA_CODEC_ID(0x14f120d0, "CX11970"), + HDA_CODEC_ID(0x14f120d1, "SN6180"), + HDA_CODEC_ID(0x14f15045, "CX20549 (Venice)"), + HDA_CODEC_ID(0x14f15047, "CX20551 (Waikiki)"), + HDA_CODEC_ID(0x14f15051, "CX20561 (Hermosa)"), + HDA_CODEC_ID(0x14f15066, "CX20582 (Pebble)"), + HDA_CODEC_ID(0x14f15067, "CX20583 (Pebble HSF)"), + HDA_CODEC_ID(0x14f15068, "CX20584"), + HDA_CODEC_ID(0x14f15069, "CX20585"), + HDA_CODEC_ID(0x14f1506c, "CX20588"), + HDA_CODEC_ID(0x14f1506e, "CX20590"), + HDA_CODEC_ID(0x14f15097, "CX20631"), + HDA_CODEC_ID(0x14f15098, "CX20632"), + HDA_CODEC_ID(0x14f150a1, "CX20641"), + HDA_CODEC_ID(0x14f150a2, "CX20642"), + HDA_CODEC_ID(0x14f150ab, "CX20651"), + HDA_CODEC_ID(0x14f150ac, "CX20652"), + HDA_CODEC_ID(0x14f150b8, "CX20664"), + HDA_CODEC_ID(0x14f150b9, "CX20665"), + HDA_CODEC_ID(0x14f150f1, "CX21722"), + HDA_CODEC_ID(0x14f150f2, "CX20722"), + HDA_CODEC_ID(0x14f150f3, "CX21724"), + HDA_CODEC_ID(0x14f150f4, "CX20724"), + HDA_CODEC_ID(0x14f1510f, "CX20751/2"), + HDA_CODEC_ID(0x14f15110, "CX20751/2"), + HDA_CODEC_ID(0x14f15111, "CX20753/4"), + HDA_CODEC_ID(0x14f15113, "CX20755"), + HDA_CODEC_ID(0x14f15114, "CX20756"), + HDA_CODEC_ID(0x14f15115, "CX20757"), + HDA_CODEC_ID(0x14f151d7, "CX20952"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant); @@ -1326,6 +1327,7 @@ MODULE_DESCRIPTION("Conexant HD-audio codec"); static struct hda_codec_driver conexant_driver = { .id = snd_hda_id_conexant, + .ops = &cx_codec_ops, }; module_hda_codec_driver(conexant_driver); -- GitLab From 3cea41383450319d0e130c6339f023f307723d6d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:21 +0200 Subject: [PATCH 649/868] ALSA: hda/senary: Rewrite to new probe method Convert the Senary codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-19-tiwai@suse.de --- sound/hda/codecs/senarytech.c | 46 ++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c index 9a253ad19f4df..9aa1e9bcd9ec0 100644 --- a/sound/hda/codecs/senarytech.c +++ b/sound/hda/codecs/senarytech.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Senary HDA audio codec + * HD audio codec driver for Senary HDA audio codec * * Initially based on conexant.c */ @@ -129,7 +129,7 @@ static void senary_init_gpio_led(struct hda_codec *codec) } } -static int senary_auto_init(struct hda_codec *codec) +static int senary_init(struct hda_codec *codec) { snd_hda_gen_init(codec); senary_init_gpio_led(codec); @@ -138,7 +138,7 @@ static int senary_auto_init(struct hda_codec *codec) return 0; } -static void senary_auto_shutdown(struct hda_codec *codec) +static void senary_shutdown(struct hda_codec *codec) { struct senary_spec *spec = codec->spec; @@ -148,29 +148,19 @@ static void senary_auto_shutdown(struct hda_codec *codec) senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); } -static void senary_auto_free(struct hda_codec *codec) +static void senary_remove(struct hda_codec *codec) { - senary_auto_shutdown(codec); - snd_hda_gen_free(codec); + senary_shutdown(codec); + snd_hda_gen_remove(codec); } -static int senary_auto_suspend(struct hda_codec *codec) +static int senary_suspend(struct hda_codec *codec) { - senary_auto_shutdown(codec); + senary_shutdown(codec); return 0; } -static const struct hda_codec_ops senary_auto_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = senary_auto_init, - .free = senary_auto_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = senary_auto_suspend, - .check_power_status = snd_hda_gen_check_power_status, -}; - -static int patch_senary_auto(struct hda_codec *codec) +static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct senary_spec *spec; int err; @@ -182,7 +172,6 @@ static int patch_senary_auto(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - codec->patch_ops = senary_auto_patch_ops; senary_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; @@ -221,15 +210,27 @@ static int patch_senary_auto(struct hda_codec *codec) return 0; error: - senary_auto_free(codec); + senary_remove(codec); return err; } +static const struct hda_codec_ops senary_codec_ops = { + .probe = senary_probe, + .remove = senary_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = senary_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = senary_suspend, + .check_power_status = snd_hda_gen_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* */ static const struct hda_device_id snd_hda_id_senary[] = { - HDA_CODEC_ENTRY(0x1fa86186, "SN6186", patch_senary_auto), + HDA_CODEC_ID(0x1fa86186, "SN6186"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary); @@ -239,6 +240,7 @@ MODULE_DESCRIPTION("Senarytech HD-audio codec"); static struct hda_codec_driver senary_driver = { .id = snd_hda_id_senary, + .ops = &senary_codec_ops, }; module_hda_codec_driver(senary_driver); -- GitLab From 51a1e7f4131ad150039d8b99088d1f7fdf1bc369 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:22 +0200 Subject: [PATCH 650/868] ALSA: hda/si3054: Rewrite to new probe method Convert the SI3054 codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-20-tiwai@suse.de --- sound/hda/codecs/si3054.c | 53 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/sound/hda/codecs/si3054.c b/sound/hda/codecs/si3054.c index 763eae80a148e..87cf9da9f3bf3 100644 --- a/sound/hda/codecs/si3054.c +++ b/sound/hda/codecs/si3054.c @@ -2,7 +2,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for Silicon Labs 3054/5 modem codec + * HD audio codec driver for Silicon Labs 3054/5 modem codec * * Copyright (c) 2005 Sasha Khapyorsky * Takashi Iwai @@ -246,50 +246,48 @@ static int si3054_init(struct hda_codec *codec) return 0; } -static void si3054_free(struct hda_codec *codec) +static void si3054_remove(struct hda_codec *codec) { kfree(codec->spec); } - /* */ -static const struct hda_codec_ops si3054_patch_ops = { - .build_controls = si3054_build_controls, - .build_pcms = si3054_build_pcms, - .init = si3054_init, - .free = si3054_free, -}; - -static int patch_si3054(struct hda_codec *codec) +static int si3054_probe(struct hda_codec *codec, const struct hda_device_id *id) { - struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) + codec->spec = kzalloc(sizeof(struct si3054_spec), GFP_KERNEL); + if (!codec->spec) return -ENOMEM; - codec->spec = spec; - codec->patch_ops = si3054_patch_ops; return 0; } +static const struct hda_codec_ops si3054_codec_ops = { + .probe = si3054_probe, + .remove = si3054_remove, + .build_controls = si3054_build_controls, + .build_pcms = si3054_build_pcms, + .init = si3054_init, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_si3054[] = { - HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054), - HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054), + HDA_CODEC_ID(0x163c3055, "Si3054"), + HDA_CODEC_ID(0x163c3155, "Si3054"), + HDA_CODEC_ID(0x11c13026, "Si3054"), + HDA_CODEC_ID(0x11c13055, "Si3054"), + HDA_CODEC_ID(0x11c13155, "Si3054"), + HDA_CODEC_ID(0x10573055, "Si3054"), + HDA_CODEC_ID(0x10573057, "Si3054"), + HDA_CODEC_ID(0x10573155, "Si3054"), /* VIA HDA on Clevo m540 */ - HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054), + HDA_CODEC_ID(0x11063288, "Si3054"), /* Asus A8J Modem (SM56) */ - HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054), + HDA_CODEC_ID(0x15433155, "Si3054"), /* LG LW20 modem */ - HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054), + HDA_CODEC_ID(0x18540018, "Si3054"), {} }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054); @@ -299,6 +297,7 @@ MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); static struct hda_codec_driver si3054_driver = { .id = snd_hda_id_si3054, + .ops = &si3054_codec_ops, }; module_hda_codec_driver(si3054_driver); -- GitLab From be60c1290967d410597a6bc6a0982dfb8b3392e3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:23 +0200 Subject: [PATCH 651/868] ALSA: hda/via: Rewrite to new probe method Convert the VIA codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-21-tiwai@suse.de --- sound/hda/codecs/via.c | 443 +++++++++++++++++------------------------ 1 file changed, 185 insertions(+), 258 deletions(-) diff --git a/sound/hda/codecs/via.c b/sound/hda/codecs/via.c index e3ce563f866b3..6becea9bb810c 100644 --- a/sound/hda/codecs/via.c +++ b/sound/hda/codecs/via.c @@ -2,7 +2,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec + * HD audio codec driver for VIA VT17xx/VT18xx/VT20xx codec * * (C) 2006-2009 VIA Technology, Inc. * (C) 2006-2008 Takashi Iwai @@ -52,8 +52,10 @@ enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, + VT1709, VT1709_10CH, VT1709_6CH, + VT1708B, VT1708B_8CH, VT1708B_4CH, VT1708S, @@ -66,6 +68,7 @@ enum VIA_HDA_CODEC { VT1802, VT1705CF, VT1808, + VT3476, CODEC_TYPES, }; @@ -95,8 +98,6 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action); -static const struct hda_codec_ops via_patch_ops; /* defined below */ - static struct via_spec *via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -118,7 +119,6 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; codec->power_save_node = 1; spec->gen.power_down_unused = 1; - codec->patch_ops = via_patch_ops; return spec; } @@ -373,10 +373,10 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, vt1708_update_hp_work(codec); } -static void via_free(struct hda_codec *codec) +static void via_remove(struct hda_codec *codec) { vt1708_stop_hp_work(codec); - snd_hda_gen_free(codec); + snd_hda_gen_remove(codec); } static int via_suspend(struct hda_codec *codec) @@ -395,7 +395,7 @@ static int via_resume(struct hda_codec *codec) { /* some delay here to make jack detection working (bko#98921) */ msleep(10); - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); return 0; } @@ -411,20 +411,6 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) /* */ -static int via_init(struct hda_codec *codec); - -static const struct hda_codec_ops via_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = via_suspend, - .resume = via_resume, - .check_power_status = via_check_power_status, -}; - - static const struct hda_verb vt1708_init_verbs[] = { /* power down jack detect function */ {0x1, 0xf81, 0x1}, @@ -541,19 +527,21 @@ static int via_init(struct hda_codec *codec) return 0; } -static int vt1708_build_controls(struct hda_codec *codec) +static int via_build_controls(struct hda_codec *codec) { /* In order not to create "Phantom Jack" controls, temporary enable jackpoll */ int err; int old_interval = codec->jackpoll_interval; - codec->jackpoll_interval = msecs_to_jiffies(100); + if (old_interval) + codec->jackpoll_interval = msecs_to_jiffies(100); err = snd_hda_gen_build_controls(codec); - codec->jackpoll_interval = old_interval; + if (old_interval) + codec->jackpoll_interval = old_interval; return err; } -static int vt1708_build_pcms(struct hda_codec *codec) +static int via_build_pcms(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i, err; @@ -580,19 +568,11 @@ static int vt1708_build_pcms(struct hda_codec *codec) return 0; } -static int patch_vt1708(struct hda_codec *codec) +static int probe_vt1708(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - - /* override some patch_ops */ - codec->patch_ops.build_controls = vt1708_build_controls; - codec->patch_ops.build_pcms = vt1708_build_pcms; spec->gen.mixer_nid = 0x17; /* set jackpoll_interval while parsing the codec */ @@ -611,81 +591,47 @@ static int patch_vt1708(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1708_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); if (err < 0) - goto error; + return err; /* add jack detect on/off control */ - if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { - err = -ENOMEM; - goto error; - } + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) + return -ENOMEM; /* clear jackpoll_interval again; it's set dynamically */ codec->jackpoll_interval = 0; return 0; - - error: - via_free(codec); - return err; } -static int patch_vt1709(struct hda_codec *codec) +static int probe_vt1709(struct hda_codec *codec) { - struct via_spec *spec; - int err; - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + struct via_spec *spec = codec->spec; spec->gen.mixer_nid = 0x18; - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -static int patch_vt1708S(struct hda_codec *codec); -static int patch_vt1708B(struct hda_codec *codec) +static int probe_vt1708S(struct hda_codec *codec); +static int probe_vt1708B(struct hda_codec *codec) { - struct via_spec *spec; - int err; + struct via_spec *spec = codec->spec; if (get_codec_type(codec) == VT1708BCE) - return patch_vt1708S(codec); - - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; + return probe_vt1708S(codec); spec->gen.mixer_nid = 0x16; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1708S */ +/* Support for VT1708S */ static const struct hda_verb vt1708S_init_verbs[] = { /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, @@ -706,16 +652,11 @@ static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, (0 << AC_AMPCAP_MUTE_SHIFT)); } -static int patch_vt1708S(struct hda_codec *codec) +static int probe_vt1708S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -729,21 +670,12 @@ static int patch_vt1708S(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1708S_init_verbs); if (err < 0) - goto error; - - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; + return err; - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1702 */ +/* Support for VT1702 */ static const struct hda_verb vt1702_init_verbs[] = { /* mixer enable */ @@ -753,16 +685,11 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -static int patch_vt1702(struct hda_codec *codec) +static int probe_vt1702(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x1a; /* limit AA path volume to 0 dB */ @@ -774,21 +701,13 @@ static int patch_vt1702(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1702_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1718S */ +/* Support for VT1718S */ static const struct hda_verb vt1718S_init_verbs[] = { /* Enable MW0 adjust Gain 5 */ @@ -836,16 +755,11 @@ static int add_secret_dac_path(struct hda_codec *codec) } -static int patch_vt1718S(struct hda_codec *codec) +static int probe_vt1718S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -853,21 +767,13 @@ static int patch_vt1718S(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1718S_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* Patch for VT1716S */ +/* Support for VT1716S */ static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -933,41 +839,30 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; -static int patch_vt1716S(struct hda_codec *codec) +static int probe_vt1716S(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); err = snd_hda_add_verbs(codec, vt1716S_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); if (err < 0) - goto error; + return err; if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || - !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { - err = -ENOMEM; - goto error; - } + !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) + return -ENOMEM; return 0; - - error: - via_free(codec); - return err; } /* for vt2002P */ @@ -1055,17 +950,12 @@ static void fix_vt1802_connections(struct hda_codec *codec) snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); } -/* patch for vt2002P */ -static int patch_vt2002P(struct hda_codec *codec) +/* Support for vt2002P */ +static int probe_vt2002P(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -1081,18 +971,10 @@ static int patch_vt2002P(struct hda_codec *codec) else err = snd_hda_add_verbs(codec, vt2002P_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } /* for vt1812 */ @@ -1105,17 +987,11 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; -/* patch for vt1812 */ -static int patch_vt1812(struct hda_codec *codec) +static int probe_vt1812(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); @@ -1123,21 +999,13 @@ static int patch_vt1812(struct hda_codec *codec) err = snd_hda_add_verbs(codec, vt1812_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; - - return 0; - - error: - via_free(codec); - return err; + return via_parse_auto_config(codec); } -/* patch for vt3476 */ +/* Support for vt3476 */ static const struct hda_verb vt3476_init_verbs[] = { /* Enable DMic 8/16/32K */ @@ -1149,96 +1017,155 @@ static const struct hda_verb vt3476_init_verbs[] = { { } }; -static int patch_vt3476(struct hda_codec *codec) +static int probe_vt3476(struct hda_codec *codec) { - struct via_spec *spec; + struct via_spec *spec = codec->spec; int err; - /* create a codec specific record */ - spec = via_new_spec(codec); - if (spec == NULL) - return -ENOMEM; - spec->gen.mixer_nid = 0x3f; add_secret_dac_path(codec); err = snd_hda_add_verbs(codec, vt3476_init_verbs); if (err < 0) - goto error; + return err; /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) - goto error; + return via_parse_auto_config(codec); - return 0; +} - error: - via_free(codec); - return err; +/* + * common driver probe + */ +static int via_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = via_new_spec(codec); + if (!spec) + return -ENOMEM; + + switch (id->driver_data) { + case VT1708: + err = probe_vt1708(codec); + break; + case VT1709: + err = probe_vt1709(codec); + break; + case VT1708B: + err = probe_vt1708B(codec); + break; + case VT1708S: + err = probe_vt1708S(codec); + break; + case VT1702: + err = probe_vt1702(codec); + break; + case VT1718S: + err = probe_vt1718S(codec); + break; + case VT1716S: + err = probe_vt1716S(codec); + break; + case VT2002P: + err = probe_vt2002P(codec); + break; + case VT1812: + err = probe_vt1812(codec); + break; + case VT3476: + err = probe_vt3476(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + via_remove(codec); + return err; + } + + return 0; } +static const struct hda_codec_ops via_codec_ops = { + .probe = via_probe, + .remove = via_remove, + .build_controls = via_build_controls, + .build_pcms = via_build_pcms, + .init = via_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = via_suspend, + .resume = via_resume, + .check_power_status = via_check_power_status, + .stream_pm = snd_hda_gen_stream_pm, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_via[] = { - HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), - HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), - HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), - HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), - HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), - HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), - HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), - HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), - HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), - HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), - HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), - HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), - HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), - HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), - HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), + HDA_CODEC_ID_MODEL(0x11061708, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x11061709, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106170a, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106170b, "VT1708", VT1708), + HDA_CODEC_ID_MODEL(0x1106e710, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e711, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e712, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e713, "VT1709 10-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e714, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e715, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e716, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e717, "VT1709 6-Ch", VT1709), + HDA_CODEC_ID_MODEL(0x1106e720, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e721, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e722, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e723, "VT1708B 8-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e724, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e725, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e726, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x1106e727, "VT1708B 4-Ch", VT1708B), + HDA_CODEC_ID_MODEL(0x11060397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11061397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11062397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11063397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11064397, "VT1705", VT1708S), + HDA_CODEC_ID_MODEL(0x11065397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11066397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11067397, "VT1708S", VT1708S), + HDA_CODEC_ID_MODEL(0x11060398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11061398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11062398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11063398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11064398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11065398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11066398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11067398, "VT1702", VT1702), + HDA_CODEC_ID_MODEL(0x11060428, "VT1718S", VT1718S), + HDA_CODEC_ID_MODEL(0x11064428, "VT1718S", VT1718S), + HDA_CODEC_ID_MODEL(0x11060441, "VT2020", VT1718S), + HDA_CODEC_ID_MODEL(0x11064441, "VT1828S", VT1718S), + HDA_CODEC_ID_MODEL(0x11060433, "VT1716S", VT1716S), + HDA_CODEC_ID_MODEL(0x1106a721, "VT1716S", VT1716S), + HDA_CODEC_ID_MODEL(0x11060438, "VT2002P", VT2002P), + HDA_CODEC_ID_MODEL(0x11064438, "VT2002P", VT2002P), + HDA_CODEC_ID_MODEL(0x11060448, "VT1812", VT1812), + HDA_CODEC_ID_MODEL(0x11060440, "VT1818S", VT1708S), + HDA_CODEC_ID_MODEL(0x11060446, "VT1802", VT2002P), + HDA_CODEC_ID_MODEL(0x11068446, "VT1802", VT2002P), + HDA_CODEC_ID_MODEL(0x11064760, "VT1705CF", VT3476), + HDA_CODEC_ID_MODEL(0x11064761, "VT1708SCE", VT3476), + HDA_CODEC_ID_MODEL(0x11064762, "VT1808", VT3476), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); static struct hda_codec_driver via_driver = { .id = snd_hda_id_via, + .ops = &via_codec_ops, }; MODULE_LICENSE("GPL"); -- GitLab From eb2f0844140fdb569e05b5521087dff6892c27cd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:24 +0200 Subject: [PATCH 652/868] ALSA: hda/sigmatel: Rewrite to new probe method Convert the Sigmatel/IDT codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-22-tiwai@suse.de --- sound/hda/codecs/sigmatel.c | 398 ++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 195 deletions(-) diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c index 56274ff49b0b7..ecbee408d771c 100644 --- a/sound/hda/codecs/sigmatel.c +++ b/sound/hda/codecs/sigmatel.c @@ -2,7 +2,7 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for SigmaTel STAC92xx + * HD audio codec driver for SigmaTel STAC92xx * * Copyright (c) 2005 Embedded Alley Solutions, Inc. * Matt Porter @@ -4391,8 +4391,6 @@ static int stac_init(struct hda_codec *codec) return 0; } -#define stac_free snd_hda_gen_free - #ifdef CONFIG_SND_PROC_FS static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) @@ -4454,15 +4452,6 @@ static int stac_suspend(struct hda_codec *codec) return 0; } -static const struct hda_codec_ops stac_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = stac_init, - .free = stac_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = stac_suspend, -}; - static int alloc_stac_spec(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4474,19 +4463,14 @@ static int alloc_stac_spec(struct hda_codec *codec) codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ spec->gen.dac_min_mute = true; - codec->patch_ops = stac_patch_ops; return 0; } -static int patch_stac9200(struct hda_codec *codec) +static int probe_stac9200(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4500,25 +4484,19 @@ static int patch_stac9200(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -static int patch_stac925x(struct hda_codec *codec) +static int probe_stac925x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4530,26 +4508,20 @@ static int patch_stac925x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } -static int patch_stac92hd73xx(struct hda_codec *codec) +static int probe_stac92hd73xx(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; int num_dacs; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; /* enable power_save_node only for new 92HD89xx chips, as it causes * click noises on old 92HD73xx chips. @@ -4604,10 +4576,8 @@ static int patch_stac92hd73xx(struct hda_codec *codec) snd_hda_add_verbs(codec, stac92hd73xx_core_init); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } /* Don't GPIO-mute speakers if there are no internal speakers, because * the GPIO might be necessary for Headphone @@ -4646,15 +4616,11 @@ static void stac_setup_gpio(struct hda_codec *codec) } } -static int patch_stac92hd83xxx(struct hda_codec *codec) +static int probe_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - /* longer delay needed for D3 */ codec->core.power_caps &= ~AC_PWRST_EPSS; @@ -4679,10 +4645,8 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd_proc_hook; @@ -4695,15 +4659,11 @@ static const hda_nid_t stac92hd95_pwr_nids[] = { 0x0a, 0x0b, 0x0c, 0x0d }; -static int patch_stac92hd95(struct hda_codec *codec) +static int probe_stac92hd95(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - /* longer delay needed for D3 */ codec->core.power_caps &= ~AC_PWRST_EPSS; @@ -4725,10 +4685,8 @@ static int patch_stac92hd95(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd_proc_hook; @@ -4737,16 +4695,12 @@ static int patch_stac92hd95(struct hda_codec *codec) return 0; } -static int patch_stac92hd71bxx(struct hda_codec *codec) +static int probe_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; /* disabled power_save_node since it causes noises on a Dell machine */ /* codec->power_save_node = 1; */ @@ -4809,10 +4763,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) stac_setup_gpio(codec); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac92hd7x_proc_hook; @@ -4821,15 +4773,11 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) return 0; } -static int patch_stac922x(struct hda_codec *codec) +static int probe_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4848,10 +4796,8 @@ static int patch_stac922x(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -4863,15 +4809,11 @@ static const char * const stac927x_spdif_labels[] = { "Analog Mux 2", "Analog Mux 3", NULL }; -static int patch_stac927x(struct hda_codec *codec) +static int probe_stac927x(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4897,10 +4839,8 @@ static int patch_stac927x(struct hda_codec *codec) snd_hda_add_verbs(codec, stac927x_core_init); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac927x_proc_hook; @@ -4921,15 +4861,11 @@ static int patch_stac927x(struct hda_codec *codec) return 0; } -static int patch_stac9205(struct hda_codec *codec) +static int probe_stac9205(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -4955,10 +4891,8 @@ static int patch_stac9205(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); + if (err < 0) return err; - } codec->proc_widget_hook = stac9205_proc_hook; @@ -5008,15 +4942,11 @@ static const struct hda_quirk stac9872_fixup_tbl[] = { {} /* terminator */ }; -static int patch_stac9872(struct hda_codec *codec) +static int probe_stac9872(struct hda_codec *codec) { struct sigmatel_spec *spec; int err; - err = alloc_stac_spec(codec); - if (err < 0) - return err; - spec = codec->spec; spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; @@ -5028,125 +4958,202 @@ static int patch_stac9872(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac_parse_auto_config(codec); - if (err < 0) { - stac_free(codec); - return -EINVAL; - } + if (err < 0) + return err; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; } +/* + * common driver probe + */ + +enum { + MODEL_STAC9200, + MODEL_STAC9205, + MODEL_STAC922X, + MODEL_STAC925X, + MODEL_STAC927X, + MODEL_STAC9872, + MODEL_STAC92HD71BXX, + MODEL_STAC92HD73XX, + MODEL_STAC92HD83XXX, + MODEL_STAC92HD95, +}; + +static int stac_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + int err; + + err = alloc_stac_spec(codec); + if (err < 0) + return err; + + switch (id->driver_data) { + case MODEL_STAC9200: + err = probe_stac9200(codec); + break; + case MODEL_STAC9205: + err = probe_stac9205(codec); + break; + case MODEL_STAC922X: + err = probe_stac922x(codec); + break; + case MODEL_STAC925X: + err = probe_stac925x(codec); + break; + case MODEL_STAC927X: + err = probe_stac927x(codec); + break; + case MODEL_STAC9872: + err = probe_stac9872(codec); + break; + case MODEL_STAC92HD71BXX: + err = probe_stac92hd71bxx(codec); + break; + case MODEL_STAC92HD73XX: + err = probe_stac92hd73xx(codec); + break; + case MODEL_STAC92HD83XXX: + err = probe_stac92hd83xxx(codec); + break; + case MODEL_STAC92HD95: + err = probe_stac92hd95(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_gen_remove(codec); + return err; + } + + return 0; +} + +static const struct hda_codec_ops stac_codec_ops = { + .probe = stac_probe, + .remove = snd_hda_gen_remove, + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = stac_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = stac_suspend, + .stream_pm = snd_hda_gen_stream_pm, +}; /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_sigmatel[] = { - HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200), - HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x), - HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), - HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), - HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x), - HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), - HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x), - HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x), - HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x), - HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x), - HDA_CODEC_ENTRY(0x83847632, "STAC9202", patch_stac925x), - HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x), - HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x), - HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x), - HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x), - HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x), + HDA_CODEC_ID_MODEL(0x83847690, "STAC9200", MODEL_STAC9200), + HDA_CODEC_ID_MODEL(0x83847882, "STAC9220 A1", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847680, "STAC9221 A1", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847880, "STAC9220 A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847681, "STAC9220D/9223D A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847682, "STAC9221 A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847683, "STAC9221D A2", MODEL_STAC922X), + HDA_CODEC_ID_MODEL(0x83847618, "STAC9227", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847619, "STAC9227", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847638, "STAC92HD700", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847616, "STAC9228", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847617, "STAC9228", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847614, "STAC9229", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847615, "STAC9229", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847620, "STAC9274", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847621, "STAC9274D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847622, "STAC9273X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847623, "STAC9273D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847624, "STAC9272X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847625, "STAC9272D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847626, "STAC9271X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847627, "STAC9271D", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847628, "STAC9274X5NH", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847629, "STAC9274D5NH", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847632, "STAC9202", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847633, "STAC9202D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847634, "STAC9250", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847635, "STAC9250D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847636, "STAC9251", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847637, "STAC9250D", MODEL_STAC925X), + HDA_CODEC_ID_MODEL(0x83847645, "92HD206X", MODEL_STAC927X), + HDA_CODEC_ID_MODEL(0x83847646, "92HD206D", MODEL_STAC927X), /* The following does not take into account .id=0x83847661 when subsys = * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are * currently not fully supported. */ - HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872), - HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872), - HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872), - HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205), - HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205), - HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95), - HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx), - HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx), - HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx), - HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx), + HDA_CODEC_ID_MODEL(0x83847661, "CXD9872RD/K", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847662, "STAC9872AK", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847664, "CXD9872AKD", MODEL_STAC9872), + HDA_CODEC_ID_MODEL(0x83847698, "STAC9205", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a0, "STAC9205", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a1, "STAC9205D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a2, "STAC9204", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a3, "STAC9204D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a4, "STAC9255", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a5, "STAC9255D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a6, "STAC9254", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x838476a7, "STAC9254D", MODEL_STAC9205), + HDA_CODEC_ID_MODEL(0x111d7603, "92HD75B3X5", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d7604, "92HD83C1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d4, "92HD83C1C5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7605, "92HD81B1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d5, "92HD81B1C5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d1, "92HD87B1/3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76d9, "92HD87B2/4", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7666, "92HD88B3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7667, "92HD88B1", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7668, "92HD88B2", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7669, "92HD88B4", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d7608, "92HD75B2X5", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d7674, "92HD73D1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7675, "92HD73C1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7676, "92HD73E1X5", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d7695, "92HD95", MODEL_STAC92HD95), + HDA_CODEC_ID_MODEL(0x111d76b0, "92HD71B8X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b1, "92HD71B8X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b2, "92HD71B7X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b3, "92HD71B7X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b4, "92HD71B6X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b5, "92HD71B6X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b6, "92HD71B5X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76b7, "92HD71B5X", MODEL_STAC92HD71BXX), + HDA_CODEC_ID_MODEL(0x111d76c0, "92HD89C3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c1, "92HD89C2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c2, "92HD89C1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c3, "92HD89B3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c4, "92HD89B2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c5, "92HD89B1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c6, "92HD89E3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c7, "92HD89E2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c8, "92HD89E1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76c9, "92HD89D3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76ca, "92HD89D2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cb, "92HD89D1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cc, "92HD89F3", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76cd, "92HD89F2", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76ce, "92HD89F1", MODEL_STAC92HD73XX), + HDA_CODEC_ID_MODEL(0x111d76df, "92HD93BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e0, "92HD91BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e3, "92HD98BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e5, "92HD99BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e7, "92HD90BXX", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e8, "92HD66B1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76e9, "92HD66B2X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ea, "92HD66B3X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76eb, "92HD66C1X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ec, "92HD66C2X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ed, "92HD66C3X5", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ee, "92HD66B1X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76ef, "92HD66B2X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f0, "92HD66B3X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f1, "92HD66C1X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f2, "92HD66C2X3", MODEL_STAC92HD83XXX), + HDA_CODEC_ID_MODEL(0x111d76f3, "92HD66C3/65", MODEL_STAC92HD83XXX), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel); @@ -5156,6 +5163,7 @@ MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); static struct hda_codec_driver sigmatel_driver = { .id = snd_hda_id_sigmatel, + .ops = &stac_codec_ops, }; module_hda_codec_driver(sigmatel_driver); -- GitLab From 6cce08122f7096c045ed72b734e2e00ab46fd111 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:25 +0200 Subject: [PATCH 653/868] ALSA: hda/ca0132: Rewrite to new probe method Convert the CA0132 codec driver to use the new hda_codec_ops probe. No functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-23-tiwai@suse.de --- sound/hda/codecs/ca0132.c | 103 +++++++++++++++++++++------------ sound/hda/codecs/ca0132_regs.h | 2 +- 2 files changed, 68 insertions(+), 37 deletions(-) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index 35a979465c751..6ab59c336ed44 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HD audio interface patch for Creative CA0132 chip + * HD audio codec driver for Creative CA0132 chip * * Copyright (c) 2011, Creative Technology Ltd. * @@ -9118,7 +9118,7 @@ static void sbz_dsp_startup_check(struct hda_codec *codec) codec_info(codec, "Reloading... Tries left: %d", reload); sbz_exit_chip(codec); spec->dsp_state = DSP_DOWNLOAD_INIT; - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); failure = 0; for (i = 0; i < 4; i++) { chipio_read(codec, cur_address, &dsp_data_check[i]); @@ -9694,30 +9694,6 @@ static void dbpro_free(struct hda_codec *codec) kfree(codec->spec); } -static int ca0132_suspend(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - - cancel_delayed_work_sync(&spec->unsol_hp_work); - return 0; -} - -static const struct hda_codec_ops ca0132_patch_ops = { - .build_controls = ca0132_build_controls, - .build_pcms = ca0132_build_pcms, - .init = ca0132_init, - .free = ca0132_free, - .unsol_event = snd_hda_jack_unsol_event, - .suspend = ca0132_suspend, -}; - -static const struct hda_codec_ops dbpro_patch_ops = { - .build_controls = dbpro_build_controls, - .build_pcms = dbpro_build_pcms, - .init = dbpro_init, - .free = dbpro_free, -}; - static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -9982,12 +9958,23 @@ static void sbz_detect_quirk(struct hda_codec *codec) } } -static int patch_ca0132(struct hda_codec *codec) +static void ca0132_codec_remove(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_free(codec); + else + return ca0132_free(codec); +} + +static int ca0132_codec_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct ca0132_spec *spec; int err; - codec_dbg(codec, "patch_ca0132\n"); + codec_dbg(codec, "%s\n", __func__); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -10000,11 +9987,6 @@ static int patch_ca0132(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_detect_quirk(codec); - if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) - codec->patch_ops = dbpro_patch_ops; - else - codec->patch_ops = ca0132_patch_ops; - codec->pcm_format_first = 1; codec->no_sticky_stream = 1; @@ -10100,15 +10082,63 @@ static int patch_ca0132(struct hda_codec *codec) return 0; error: - ca0132_free(codec); + ca0132_codec_remove(codec); return err; } +static int ca0132_codec_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_build_controls(codec); + else + return ca0132_build_controls(codec); +} + +static int ca0132_codec_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_build_pcms(codec); + else + return ca0132_build_pcms(codec); +} + +static int ca0132_codec_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + return dbpro_init(codec); + else + return ca0132_init(codec); +} + +static int ca0132_codec_suspend(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + cancel_delayed_work_sync(&spec->unsol_hp_work); + return 0; +} + +static const struct hda_codec_ops ca0132_codec_ops = { + .probe = ca0132_codec_probe, + .remove = ca0132_codec_remove, + .build_controls = ca0132_codec_build_controls, + .build_pcms = ca0132_codec_build_pcms, + .init = ca0132_codec_init, + .unsol_event = snd_hda_jack_unsol_event, + .suspend = ca0132_codec_suspend, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_ca0132[] = { - HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132), + HDA_CODEC_ID(0x11020011, "CA0132"), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132); @@ -10118,6 +10148,7 @@ MODULE_DESCRIPTION("Creative Sound Core3D codec"); static struct hda_codec_driver ca0132_driver = { .id = snd_hda_id_ca0132, + .ops = &ca0132_codec_ops, }; module_hda_codec_driver(ca0132_driver); diff --git a/sound/hda/codecs/ca0132_regs.h b/sound/hda/codecs/ca0132_regs.h index 0ead571fb447c..dc0153df3d5c9 100644 --- a/sound/hda/codecs/ca0132_regs.h +++ b/sound/hda/codecs/ca0132_regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * HD audio interface patch for Creative CA0132 chip. + * HD audio codec driver for Creative CA0132 chip. * CA0132 registers defines. * * Copyright (c) 2011, Creative Technology Ltd. -- GitLab From ad781b550f9a8829e3dae4bd3d18c4a126a53d04 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:26 +0200 Subject: [PATCH 654/868] ALSA: hda/hdmi: Rewrite to new probe method Convert the HDMI codec drivers to use the new hda_codec_ops probe. The Intel and Nvidia-MCP HDMI drivers needed slightly more changes to deal with the unified callbacks among all models. Also another non-trivial change is Intel driver's set_power_state callback. An additional NULL check of codec->spec is needed there since the set_power_state() may be called before the probe gets called (e.g. in ASoC hda codec hda_codec_probe()). Other than that, no functional changes. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-24-tiwai@suse.de --- sound/hda/codecs/hdmi/atihdmi.c | 31 +++-- sound/hda/codecs/hdmi/hdmi.c | 126 ++++++++++---------- sound/hda/codecs/hdmi/hdmi_local.h | 10 +- sound/hda/codecs/hdmi/intelhdmi.c | 185 ++++++++++++++++++----------- sound/hda/codecs/hdmi/nvhdmi-mcp.c | 96 +++++++-------- sound/hda/codecs/hdmi/nvhdmi.c | 183 ++++++++++++++-------------- sound/hda/codecs/hdmi/simplehdmi.c | 58 +++++---- sound/hda/codecs/hdmi/tegrahdmi.c | 69 ++++++----- 8 files changed, 422 insertions(+), 336 deletions(-) diff --git a/sound/hda/codecs/hdmi/atihdmi.c b/sound/hda/codecs/hdmi/atihdmi.c index e23995cc1b8c3..44366f75de336 100644 --- a/sound/hda/codecs/hdmi/atihdmi.c +++ b/sound/hda/codecs/hdmi/atihdmi.c @@ -528,19 +528,16 @@ static const struct drm_audio_component_audio_ops atihdmi_audio_ops = { .master_unbind = snd_hda_hdmi_acomp_master_unbind, }; -static int patch_atihdmi(struct hda_codec *codec) +static int atihdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) { struct hdmi_spec *spec; struct hdmi_spec_per_cvt *per_cvt; int err, cvt_idx; - err = patch_generic_hdmi(codec); - + err = snd_hda_hdmi_generic_probe(codec); if (err) return err; - codec->patch_ops.init = atihdmi_init; - spec = codec->spec; spec->static_pcm_mapping = true; @@ -583,15 +580,26 @@ static int patch_atihdmi(struct hda_codec *codec) return 0; } +static const struct hda_codec_ops atihdmi_codec_ops = { + .probe = atihdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = atihdmi_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + /* - * patch entries + * driver entries */ static const struct hda_device_id snd_hda_id_atihdmi[] = { -HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI", patch_atihdmi), -HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI", patch_atihdmi), -{} /* terminator */ + HDA_CODEC_ID(0x1002793c, "RS600 HDMI"), + HDA_CODEC_ID(0x10027919, "RS600 HDMI"), + HDA_CODEC_ID(0x1002791a, "RS690/780 HDMI"), + HDA_CODEC_ID(0x1002aa01, "R6xx HDMI"), + {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_atihdmi); @@ -601,6 +609,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); static struct hda_codec_driver atihdmi_driver = { .id = snd_hda_id_atihdmi, + .ops = &atihdmi_codec_ops, }; module_hda_codec_driver(atihdmi_driver); diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index 85aaa454072fc..b5d840d9892b9 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2087,7 +2087,7 @@ void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec) } EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_spec_free, "SND_HDA_CODEC_HDMI"); -void snd_hda_hdmi_generic_free(struct hda_codec *codec) +void snd_hda_hdmi_generic_remove(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx, pcm_idx; @@ -2113,7 +2113,7 @@ void snd_hda_hdmi_generic_free(struct hda_codec *codec) snd_hda_hdmi_generic_spec_free(codec); } -EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_free, "SND_HDA_CODEC_HDMI"); +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_remove, "SND_HDA_CODEC_HDMI"); int snd_hda_hdmi_generic_suspend(struct hda_codec *codec) { @@ -2133,7 +2133,7 @@ int snd_hda_hdmi_generic_resume(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; int pin_idx; - codec->patch_ops.init(codec); + snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { @@ -2144,16 +2144,6 @@ int snd_hda_hdmi_generic_resume(struct hda_codec *codec) } EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_resume, "SND_HDA_CODEC_HDMI"); -static const struct hda_codec_ops generic_hdmi_patch_ops = { - .init = snd_hda_hdmi_generic_init, - .free = snd_hda_hdmi_generic_free, - .build_pcms = snd_hda_hdmi_generic_build_pcms, - .build_controls = snd_hda_hdmi_generic_build_controls, - .unsol_event = snd_hda_hdmi_generic_unsol_event, - .suspend = snd_hda_hdmi_generic_suspend, - .resume = snd_hda_hdmi_generic_resume, -}; - static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = hdmi_pin_get_eld, .pin_setup_infoframe = hdmi_pin_setup_infoframe, @@ -2185,14 +2175,12 @@ int snd_hda_hdmi_generic_alloc(struct hda_codec *codec) codec->spec = spec; hdmi_array_init(spec, 4); - codec->patch_ops = generic_hdmi_patch_ops; - return 0; } EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_alloc, "SND_HDA_CODEC_HDMI"); /* generic HDMI parser */ -int patch_generic_hdmi(struct hda_codec *codec) +int snd_hda_hdmi_generic_probe(struct hda_codec *codec) { int err; @@ -2209,7 +2197,7 @@ int patch_generic_hdmi(struct hda_codec *codec) snd_hda_hdmi_generic_init_per_pins(codec); return 0; } -EXPORT_SYMBOL_NS_GPL(patch_generic_hdmi, "SND_HDA_CODEC_HDMI"); +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_probe, "SND_HDA_CODEC_HDMI"); /* * generic audio component binding @@ -2346,65 +2334,83 @@ EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI"); /* */ -static int patch_gf_hdmi(struct hda_codec *codec) +enum { + MODEL_GENERIC, + MODEL_GF, +}; + +static int generichdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) { int err; - err = patch_generic_hdmi(codec); - if (err) + err = snd_hda_hdmi_generic_probe(codec); + if (err < 0) return err; - /* * Glenfly GPUs have two codecs, stream switches from one codec to * another, need to do actual clean-ups in codec_cleanup_stream */ - codec->no_sticky_stream = 1; + if (id->driver_data == MODEL_GF) + codec->no_sticky_stream = 1; + return 0; } +static const struct hda_codec_ops generichdmi_codec_ops = { + .probe = generichdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + /* - * patch entries */ -static const struct hda_device_id snd_hda_id_hdmi[] = { -HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x67663d82, "Arise 82 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d83, "Arise 83 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d84, "Arise 84 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d85, "Arise 85 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d86, "Arise 86 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x67663d87, "Arise 87 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x1d179f86, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f87, "ZX-100S HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f88, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f89, "KX-5000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8a, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8b, "KX-6000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8c, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8d, "KX-6000G HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8e, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f8f, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x1d179f90, "KX-7000 HDMI/DP", patch_gf_hdmi), -HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), -/* special ID for generic HDMI */ -HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), -{} /* terminator */ +static const struct hda_device_id snd_hda_id_generichdmi[] = { + HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x11069f85, "VX11 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x17e80047, "Chrontel HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x1d179f86, "ZX-100S HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f87, "ZX-100S HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f88, "KX-5000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f89, "KX-5000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8a, "KX-6000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8b, "KX-6000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8c, "KX-6000G HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8d, "KX-6000G HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8e, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f8f, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x1d179f90, "KX-7000 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d82, "Arise 82 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d83, "Arise 83 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d84, "Arise 84 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d85, "Arise 85 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d86, "Arise 86 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x67663d87, "Arise 87 HDMI/DP", MODEL_GF), + HDA_CODEC_ID_MODEL(0x80862801, "Bearlake HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862802, "Cantiga HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862803, "Eaglelake HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x80862880, "CedarTrail HDMI", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x808629fb, "Crestline HDMI", MODEL_GENERIC), + /* special ID for generic HDMI */ + HDA_CODEC_ID_MODEL(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", MODEL_GENERIC), + {} /* terminator */ }; -MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi); +MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generichdmi); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HDMI HD-audio codec"); +MODULE_DESCRIPTION("Generic HDMI HD-audio codec"); -static struct hda_codec_driver hdmi_driver = { - .id = snd_hda_id_hdmi, +static struct hda_codec_driver generichdmi_driver = { + .id = snd_hda_id_generichdmi, + .ops = &generichdmi_codec_ops, }; -module_hda_codec_driver(hdmi_driver); +module_hda_codec_driver(generichdmi_driver); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h index 96351bebbc1b4..0654013f1fdac 100644 --- a/sound/hda/codecs/hdmi/hdmi_local.h +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -237,8 +237,8 @@ union audio_infoframe { /* Generic HDMI codec support */ int snd_hda_hdmi_generic_alloc(struct hda_codec *codec); int snd_hda_hdmi_parse_codec(struct hda_codec *codec); -int patch_generic_hdmi(struct hda_codec *codec); -void snd_hda_hdmi_generic_free(struct hda_codec *codec); +int snd_hda_hdmi_generic_probe(struct hda_codec *codec); +void snd_hda_hdmi_generic_remove(struct hda_codec *codec); int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec); int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec); @@ -286,9 +286,9 @@ void snd_hda_hdmi_acomp_master_unbind(struct device *dev, struct drm_audio_component *acomp); /* Simple / legacy HDMI codec support */ -int patch_simple_hdmi(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t pin_nid); -void snd_hda_hdmi_simple_free(struct hda_codec *codec); +int snd_hda_hdmi_simple_probe(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid); +void snd_hda_hdmi_simple_remove(struct hda_codec *codec); int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec); int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec); diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c index a88ac1f80db6a..23237d5274302 100644 --- a/sound/hda/codecs/hdmi/intelhdmi.c +++ b/sound/hda/codecs/hdmi/intelhdmi.c @@ -18,6 +18,16 @@ IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM); module_param(enable_silent_stream, bool, 0644); MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices"); +enum { + MODEL_HSW, + MODEL_GLK, + MODEL_ICL, + MODEL_TGL, + MODEL_ADLP, + MODEL_BYT, + MODEL_CPT, +}; + #define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 #define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ @@ -67,9 +77,12 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { - if (power_state == AC_PWRST_D0) { - intel_haswell_enable_all_pins(codec, false); - intel_haswell_fixup_enable_dp12(codec); + /* check codec->spec: it can be called before the probe gets called */ + if (codec->spec) { + if (power_state == AC_PWRST_D0) { + intel_haswell_enable_all_pins(codec, false); + intel_haswell_fixup_enable_dp12(codec); + } } snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); @@ -456,13 +469,15 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec, } } -static int i915_adlp_hdmi_suspend(struct hda_codec *codec) +static int i915_hdmi_suspend(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; bool silent_streams = false; int pin_idx, res; res = snd_hda_hdmi_generic_suspend(codec); + if (spec->silent_stream_type != SILENT_STREAM_KAE) + return res; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -495,12 +510,14 @@ static int i915_adlp_hdmi_suspend(struct hda_codec *codec) return res; } -static int i915_adlp_hdmi_resume(struct hda_codec *codec) +static int i915_hdmi_resume(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int pin_idx, res; res = snd_hda_hdmi_generic_resume(codec); + if (spec->silent_stream_type != SILENT_STREAM_KAE) + return res; /* KAE not programmed at suspend, nothing to do here */ if (!codec->no_stream_clean_at_suspend) @@ -539,8 +556,6 @@ static int i915_adlp_hdmi_resume(struct hda_codec *codec) /* precondition and allocation for Intel codecs */ static int alloc_intel_hdmi(struct hda_codec *codec) { - int err; - /* requires i915 binding */ if (!codec->bus->core.audio_component) { codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); @@ -549,12 +564,7 @@ static int alloc_intel_hdmi(struct hda_codec *codec) return -ENODEV; } - err = snd_hda_hdmi_generic_alloc(codec); - if (err < 0) - return err; - /* no need to handle unsol events */ - codec->patch_ops.unsol_event = NULL; - return 0; + return snd_hda_hdmi_generic_alloc(codec); } /* parse and post-process for Intel codecs */ @@ -580,11 +590,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, bool send_silent_stream) { struct hdmi_spec *spec; - int err; - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; spec = codec->spec; codec->dp_mst = true; spec->vendor_nid = vendor_nid; @@ -598,7 +604,6 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, codec->display_power_control = 1; - codec->patch_ops.set_power_state = haswell_set_power_state; codec->depop_delay = 0; codec->auto_runtime_pm = 1; @@ -616,13 +621,13 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid, return parse_intel_hdmi(codec); } -static int patch_i915_hsw_hdmi(struct hda_codec *codec) +static int probe_i915_hsw_hdmi(struct hda_codec *codec) { return intel_hsw_common_init(codec, 0x08, NULL, 0, 3, enable_silent_stream); } -static int patch_i915_glk_hdmi(struct hda_codec *codec) +static int probe_i915_glk_hdmi(struct hda_codec *codec) { /* * Silent stream calls audio component .get_power() from @@ -632,7 +637,7 @@ static int patch_i915_glk_hdmi(struct hda_codec *codec) return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false); } -static int patch_i915_icl_hdmi(struct hda_codec *codec) +static int probe_i915_icl_hdmi(struct hda_codec *codec) { /* * pin to port mapping table where the value indicate the pin number and @@ -644,7 +649,7 @@ static int patch_i915_icl_hdmi(struct hda_codec *codec) enable_silent_stream); } -static int patch_i915_tgl_hdmi(struct hda_codec *codec) +static int probe_i915_tgl_hdmi(struct hda_codec *codec) { /* * pin to port mapping table where the value indicate the pin number and @@ -656,35 +661,27 @@ static int patch_i915_tgl_hdmi(struct hda_codec *codec) enable_silent_stream); } -static int patch_i915_adlp_hdmi(struct hda_codec *codec) +static int probe_i915_adlp_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; int res; - res = patch_i915_tgl_hdmi(codec); + res = probe_i915_tgl_hdmi(codec); if (!res) { spec = codec->spec; - if (spec->silent_stream_type) { + if (spec->silent_stream_type) spec->silent_stream_type = SILENT_STREAM_KAE; - - codec->patch_ops.resume = i915_adlp_hdmi_resume; - codec->patch_ops.suspend = i915_adlp_hdmi_suspend; - } } return res; } /* Intel Baytrail and Braswell; with eld notifier */ -static int patch_i915_byt_hdmi(struct hda_codec *codec) +static int probe_i915_byt_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; - int err; - err = alloc_intel_hdmi(codec); - if (err < 0) - return err; spec = codec->spec; /* For Valleyview/Cherryview, only the display codec is in the display @@ -701,51 +698,104 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) } /* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ -static int patch_i915_cpt_hdmi(struct hda_codec *codec) +static int probe_i915_cpt_hdmi(struct hda_codec *codec) +{ + return parse_intel_hdmi(codec); +} + +/* + * common driver probe + */ +static int intelhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) { int err; err = alloc_intel_hdmi(codec); if (err < 0) return err; - return parse_intel_hdmi(codec); + + switch (id->driver_data) { + case MODEL_HSW: + err = probe_i915_hsw_hdmi(codec); + break; + case MODEL_GLK: + err = probe_i915_glk_hdmi(codec); + break; + case MODEL_ICL: + err = probe_i915_icl_hdmi(codec); + break; + case MODEL_TGL: + err = probe_i915_tgl_hdmi(codec); + break; + case MODEL_ADLP: + err = probe_i915_adlp_hdmi(codec); + break; + case MODEL_BYT: + err = probe_i915_byt_hdmi(codec); + break; + case MODEL_CPT: + err = probe_i915_cpt_hdmi(codec); + break; + default: + err = -EINVAL; + break; + } + + if (err < 0) { + snd_hda_hdmi_generic_spec_free(codec); + return err; + } + + return 0; } +static const struct hda_codec_ops intelhdmi_codec_ops = { + .probe = intelhdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = i915_hdmi_suspend, + .resume = i915_hdmi_resume, + .set_power_state = haswell_set_power_state, +}; + /* * driver entries */ static const struct hda_device_id snd_hda_id_intelhdmi[] = { -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862800, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), -HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), -HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862818, "Raptorlake HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), -HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281e, "Battlemage HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862822, "Panther Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862823, "Wildcat Lake HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), -{} /* terminator */ + HDA_CODEC_ID_MODEL(0x80860054, "IbexPeak HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862800, "Geminilake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x80862804, "IbexPeak HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862805, "CougarPoint HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862806, "PantherPoint HDMI", MODEL_CPT), + HDA_CODEC_ID_MODEL(0x80862807, "Haswell HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x80862808, "Broadwell HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x80862809, "Skylake HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280a, "Broxton HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280b, "Kabylake HDMI", MODEL_HSW), + HDA_CODEC_ID_MODEL(0x8086280c, "Cannonlake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x8086280d, "Geminilake HDMI", MODEL_GLK), + HDA_CODEC_ID_MODEL(0x8086280f, "Icelake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x80862812, "Tigerlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862814, "DG1 HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862815, "Alderlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862816, "Rocketlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862818, "Raptorlake HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x80862819, "DG2 HDMI", MODEL_TGL), + HDA_CODEC_ID_MODEL(0x8086281a, "Jasperlake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x8086281b, "Elkhartlake HDMI", MODEL_ICL), + HDA_CODEC_ID_MODEL(0x8086281c, "Alderlake-P HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281d, "Meteor Lake HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281e, "Battlemage HDMI", MODEL_ADLP), + HDA_CODEC_ID_MODEL(0x8086281f, "Raptor Lake P HDMI", MODEL_ADLP), + 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(0x80862882, "Valleyview2 HDMI", MODEL_BYT), + HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT), + {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_intelhdmi); @@ -755,6 +805,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); static struct hda_codec_driver intelhdmi_driver = { .id = snd_hda_id_intelhdmi, + .ops = &intelhdmi_codec_ops, }; module_hda_codec_driver(intelhdmi_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c index 67e187a351d7b..fbcea6d1850e6 100644 --- a/sound/hda/codecs/hdmi/nvhdmi-mcp.c +++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c @@ -12,6 +12,8 @@ #include "hda_local.h" #include "hdmi_local.h" +enum { MODEL_2CH, MODEL_8CH }; + #define Nv_VERB_SET_Channel_Allocation 0xF79 #define Nv_VERB_SET_Info_Frame_Checksum 0xF7A #define Nv_VERB_SET_Audio_Protection_On 0xF98 @@ -45,15 +47,14 @@ static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = { {} /* terminator */ }; -static int nvhdmi_7x_init_2ch(struct hda_codec *codec) +static int nvhdmi_mcp_init(struct hda_codec *codec) { - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); - return 0; -} + struct hdmi_spec *spec = codec->spec; -static int nvhdmi_7x_init_8ch(struct hda_codec *codec) -{ - snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); + if (spec->multiout.max_channels == 2) + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch); + else + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch); return 0; } @@ -233,31 +234,13 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { }, }; -static int patch_nvhdmi_2ch(struct hda_codec *codec) -{ - struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x, - nvhdmi_master_pin_nid_7x); - if (err < 0) - return err; - - codec->patch_ops.init = nvhdmi_7x_init_2ch; - /* override the PCM rates, etc, as the codec doesn't give full list */ - spec = codec->spec; - spec->pcm_playback.rates = SUPPORTED_RATES; - spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; - spec->pcm_playback.formats = SUPPORTED_FORMATS; - spec->nv_dp_workaround = true; - return 0; -} - -static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) +static int nvhdmi_mcp_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err; err = snd_hda_hdmi_simple_build_pcms(codec); - if (!err) { + if (!err && spec->multiout.max_channels == 8) { struct hda_pcm *info = get_pcm_rec(spec, 0); info->own_chmap = true; @@ -265,7 +248,7 @@ static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec) return err; } -static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) +static int nvhdmi_mcp_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; struct hda_pcm *info; @@ -276,6 +259,9 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec) if (err < 0) return err; + if (spec->multiout.max_channels != 8) + return 0; + /* add channel maps */ info = get_pcm_rec(spec, 0); err = snd_pcm_add_chmap_ctls(info->pcm, @@ -316,20 +302,29 @@ static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = { .mask = 0, }; -static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) +static int nvhdmi_mcp_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct hdmi_spec *spec; int err; - err = patch_nvhdmi_2ch(codec); + err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x, + nvhdmi_master_pin_nid_7x); if (err < 0) return err; + + /* override the PCM rates, etc, as the codec doesn't give full list */ spec = codec->spec; + spec->pcm_playback.rates = SUPPORTED_RATES; + spec->pcm_playback.maxbps = SUPPORTED_MAXBPS; + spec->pcm_playback.formats = SUPPORTED_FORMATS; + spec->nv_dp_workaround = true; + + if (id->driver_data == MODEL_2CH) + return 0; + spec->multiout.max_channels = 8; spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x; - codec->patch_ops.init = nvhdmi_7x_init_8ch; - codec->patch_ops.build_pcms = nvhdmi_7x_8ch_build_pcms; - codec->patch_ops.build_controls = nvhdmi_7x_8ch_build_controls; switch (codec->preset->vendor_id) { case 0x10de0002: @@ -353,21 +348,27 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) return 0; } -/* - * patch entries - */ +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, + .init = nvhdmi_mcp_init, + .unsol_event = snd_hda_hdmi_simple_unsol_event, +}; + static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = { -HDA_CODEC_ENTRY(0x10de0001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI", patch_nvhdmi_8ch_7x), -HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), -HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), -{} /* terminator */ + HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH), + HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH), + HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH), + {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp); @@ -377,6 +378,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); static struct hda_codec_driver nvhdmi_mcp_driver = { .id = snd_hda_id_nvhdmi_mcp, + .ops = &nvhdmi_mcp_codec_ops, }; module_hda_codec_driver(nvhdmi_mcp_driver); diff --git a/sound/hda/codecs/hdmi/nvhdmi.c b/sound/hda/codecs/hdmi/nvhdmi.c index 2add5f59daf5d..b513253b11010 100644 --- a/sound/hda/codecs/hdmi/nvhdmi.c +++ b/sound/hda/codecs/hdmi/nvhdmi.c @@ -13,6 +13,11 @@ #include "hda_local.h" #include "hdmi_local.h" +enum { + MODEL_GENERIC, + MODEL_LEGACY, +}; + /* * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on: * - 0x10de0015 @@ -61,7 +66,7 @@ static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = { .master_unbind = snd_hda_hdmi_acomp_master_unbind, }; -static int patch_nvhdmi(struct hda_codec *codec) +static int probe_generic(struct hda_codec *codec) { struct hdmi_spec *spec; int err; @@ -95,12 +100,12 @@ static int patch_nvhdmi(struct hda_codec *codec) return 0; } -static int patch_nvhdmi_legacy(struct hda_codec *codec) +static int probe_legacy(struct hda_codec *codec) { struct hdmi_spec *spec; int err; - err = patch_generic_hdmi(codec); + err = snd_hda_hdmi_generic_probe(codec); if (err) return err; @@ -117,93 +122,92 @@ static int patch_nvhdmi_legacy(struct hda_codec *codec) return 0; } -/* - * patch entries - */ +static int nvhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id) +{ + if (id->driver_data == MODEL_LEGACY) + return probe_legacy(codec); + else + return probe_generic(codec); +} + +static const struct hda_codec_ops nvhdmi_codec_ops = { + .probe = nvhdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = snd_hda_hdmi_generic_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + static const struct hda_device_id snd_hda_id_nvhdmi[] = { -HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP", patch_nvhdmi_legacy), -/* 17 is known to be absent */ -HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP", patch_nvhdmi_legacy), -HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0045, "GPU 45 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0050, "GPU 50 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0052, "GPU 52 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0061, "GPU 61 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0062, "GPU 62 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0073, "GPU 73 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0074, "GPU 74 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0076, "GPU 76 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007b, "GPU 7b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007c, "GPU 7c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de007e, "GPU 7e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0081, "GPU 81 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0084, "GPU 84 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0090, "GPU 90 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0091, "GPU 91 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0092, "GPU 92 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0093, "GPU 93 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0094, "GPU 94 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009b, "GPU 9b HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009c, "GPU 9c HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a1, "GPU a1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a3, "GPU a3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a4, "GPU a4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a5, "GPU a5 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a6, "GPU a6 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a7, "GPU a7 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a8, "GPU a8 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00a9, "GPU a9 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00aa, "GPU aa HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ab, "GPU ab HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ad, "GPU ad HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00ae, "GPU ae HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00af, "GPU af HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00b0, "GPU b0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00b1, "GPU b1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c0, "GPU c0 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c1, "GPU c1 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c3, "GPU c3 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c4, "GPU c4 HDMI/DP", patch_nvhdmi), -HDA_CODEC_ENTRY(0x10de00c5, "GPU c5 HDMI/DP", patch_nvhdmi), -{} /* terminator */ + HDA_CODEC_ID_MODEL(0x10de0008, "GPU 08 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0009, "GPU 09 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000a, "GPU 0a HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000b, "GPU 0b HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000c, "MCP89 HDMI", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de000d, "GPU 0d HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0010, "GPU 10 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0011, "GPU 11 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0012, "GPU 12 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0013, "GPU 13 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0014, "GPU 14 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0015, "GPU 15 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0016, "GPU 16 HDMI/DP", MODEL_LEGACY), + /* 17 is known to be absent */ + HDA_CODEC_ID_MODEL(0x10de0018, "GPU 18 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0019, "GPU 19 HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001a, "GPU 1a HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001b, "GPU 1b HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de001c, "GPU 1c HDMI/DP", MODEL_LEGACY), + HDA_CODEC_ID_MODEL(0x10de0040, "GPU 40 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0041, "GPU 41 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0042, "GPU 42 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0043, "GPU 43 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0044, "GPU 44 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0045, "GPU 45 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0050, "GPU 50 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0051, "GPU 51 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0052, "GPU 52 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0060, "GPU 60 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0061, "GPU 61 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0062, "GPU 62 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0070, "GPU 70 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0071, "GPU 71 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0072, "GPU 72 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0073, "GPU 73 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0074, "GPU 74 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0076, "GPU 76 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007b, "GPU 7b HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007c, "GPU 7c HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007d, "GPU 7d HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de007e, "GPU 7e HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0080, "GPU 80 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0081, "GPU 81 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0082, "GPU 82 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0083, "GPU 83 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0084, "GPU 84 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0090, "GPU 90 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0091, "GPU 91 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0092, "GPU 92 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0093, "GPU 93 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0094, "GPU 94 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0095, "GPU 95 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0097, "GPU 97 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0098, "GPU 98 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de0099, "GPU 99 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009a, "GPU 9a HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009d, "GPU 9d HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009e, "GPU 9e HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de009f, "GPU 9f HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a0, "GPU a0 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a3, "GPU a3 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a4, "GPU a4 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a5, "GPU a5 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a6, "GPU a6 HDMI/DP", MODEL_GENERIC), + HDA_CODEC_ID_MODEL(0x10de00a7, "GPU a7 HDMI/DP", MODEL_GENERIC), + {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi); @@ -213,6 +217,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); static struct hda_codec_driver nvhdmi_driver = { .id = snd_hda_id_nvhdmi, + .ops = &nvhdmi_codec_ops, }; module_hda_codec_driver(nvhdmi_driver); diff --git a/sound/hda/codecs/hdmi/simplehdmi.c b/sound/hda/codecs/hdmi/simplehdmi.c index 87ea997a6d3b0..193c8dc882af8 100644 --- a/sound/hda/codecs/hdmi/simplehdmi.c +++ b/sound/hda/codecs/hdmi/simplehdmi.c @@ -107,7 +107,7 @@ int snd_hda_hdmi_simple_init(struct hda_codec *codec) } EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_init, "SND_HDA_CODEC_HDMI"); -void snd_hda_hdmi_simple_free(struct hda_codec *codec) +void snd_hda_hdmi_simple_remove(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -115,7 +115,7 @@ void snd_hda_hdmi_simple_free(struct hda_codec *codec) snd_array_free(&spec->cvts); kfree(spec); } -EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_free, "SND_HDA_CODEC_HDMI"); +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_remove, "SND_HDA_CODEC_HDMI"); int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -168,16 +168,8 @@ static const struct hda_pcm_stream simple_pcm_playback = { }, }; -static const struct hda_codec_ops simple_hdmi_patch_ops = { - .build_controls = snd_hda_hdmi_simple_build_controls, - .build_pcms = snd_hda_hdmi_simple_build_pcms, - .init = snd_hda_hdmi_simple_init, - .free = snd_hda_hdmi_simple_free, - .unsol_event = snd_hda_hdmi_simple_unsol_event, -}; - -int patch_simple_hdmi(struct hda_codec *codec, - hda_nid_t cvt_nid, hda_nid_t pin_nid) +int snd_hda_hdmi_simple_probe(struct hda_codec *codec, + hda_nid_t cvt_nid, hda_nid_t pin_nid) { struct hdmi_spec *spec; struct hdmi_spec_per_cvt *per_cvt; @@ -200,35 +192,52 @@ int patch_simple_hdmi(struct hda_codec *codec, per_pin = snd_array_new(&spec->pins); per_cvt = snd_array_new(&spec->cvts); if (!per_pin || !per_cvt) { - snd_hda_hdmi_simple_free(codec); + snd_hda_hdmi_simple_remove(codec); return -ENOMEM; } per_cvt->cvt_nid = cvt_nid; per_pin->pin_nid = pin_nid; spec->pcm_playback = simple_pcm_playback; - codec->patch_ops = simple_hdmi_patch_ops; - return 0; } -EXPORT_SYMBOL_NS_GPL(patch_simple_hdmi, "SND_HDA_CODEC_HDMI"); +EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_probe, "SND_HDA_CODEC_HDMI"); + +/* + * driver entries + */ + +enum { MODEL_VIA }; /* VIA HDMI Implementation */ #define VIAHDMI_CVT_NID 0x02 /* audio converter1 */ #define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */ -static int patch_via_hdmi(struct hda_codec *codec) +static int simplehdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) { - return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID); + switch (id->driver_data) { + case MODEL_VIA: + return snd_hda_hdmi_simple_probe(codec, VIAHDMI_CVT_NID, + VIAHDMI_PIN_NID); + default: + return -EINVAL; + } } -/* - * patch entries - */ +static const struct hda_codec_ops simplehdmi_codec_ops = { + .probe = simplehdmi_probe, + .remove = snd_hda_hdmi_simple_remove, + .build_controls = snd_hda_hdmi_simple_build_controls, + .build_pcms = snd_hda_hdmi_simple_build_pcms, + .init = snd_hda_hdmi_simple_init, + .unsol_event = snd_hda_hdmi_simple_unsol_event, +}; + static const struct hda_device_id snd_hda_id_simplehdmi[] = { -HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), -HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), -{} /* terminator */ + HDA_CODEC_ID_MODEL(0x11069f80, "VX900 HDMI/DP", MODEL_VIA), + HDA_CODEC_ID_MODEL(0x11069f81, "VX900 HDMI/DP", MODEL_VIA), + {} /* terminator */ }; MODULE_LICENSE("GPL"); @@ -236,6 +245,7 @@ MODULE_DESCRIPTION("Simple HDMI HD-audio codec support"); static struct hda_codec_driver simplehdmi_driver = { .id = snd_hda_id_simplehdmi, + .ops = &simplehdmi_codec_ops, }; module_hda_codec_driver(simplehdmi_driver); diff --git a/sound/hda/codecs/hdmi/tegrahdmi.c b/sound/hda/codecs/hdmi/tegrahdmi.c index c13a637887996..f1f745187f68a 100644 --- a/sound/hda/codecs/hdmi/tegrahdmi.c +++ b/sound/hda/codecs/hdmi/tegrahdmi.c @@ -13,6 +13,11 @@ #include "hda_local.h" #include "hdmi_local.h" +enum { + MODEL_TEGRA, + MODEL_TEGRA234, +}; + /* * The HDA codec on NVIDIA Tegra contains two scratch registers that are * accessed using vendor-defined verbs. These registers can be used for @@ -241,7 +246,6 @@ static int tegra_hdmi_init(struct hda_codec *codec) snd_hda_hdmi_generic_init_per_pins(codec); codec->depop_delay = 10; - codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; @@ -254,18 +258,8 @@ static int tegra_hdmi_init(struct hda_codec *codec) return 0; } -static int patch_tegra_hdmi(struct hda_codec *codec) -{ - int err; - - err = snd_hda_hdmi_generic_alloc(codec); - if (err < 0) - return err; - - return tegra_hdmi_init(codec); -} - -static int patch_tegra234_hdmi(struct hda_codec *codec) +static int tegrahdmi_probe(struct hda_codec *codec, + const struct hda_device_id *id) { struct hdmi_spec *spec; int err; @@ -274,31 +268,39 @@ static int patch_tegra234_hdmi(struct hda_codec *codec) if (err < 0) return err; - codec->dp_mst = true; - spec = codec->spec; - spec->dyn_pin_out = true; - spec->hdmi_intr_trig_ctrl = true; + if (id->driver_data == MODEL_TEGRA234) { + codec->dp_mst = true; + spec = codec->spec; + spec->dyn_pin_out = true; + spec->hdmi_intr_trig_ctrl = true; + } return tegra_hdmi_init(codec); } -/* - * patch entries - */ +static const struct hda_codec_ops tegrahdmi_codec_ops = { + .probe = tegrahdmi_probe, + .remove = snd_hda_hdmi_generic_remove, + .init = snd_hda_hdmi_generic_init, + .build_pcms = tegra_hdmi_build_pcms, + .build_controls = snd_hda_hdmi_generic_build_controls, + .unsol_event = snd_hda_hdmi_generic_unsol_event, + .suspend = snd_hda_hdmi_generic_suspend, + .resume = snd_hda_hdmi_generic_resume, +}; + static const struct hda_device_id snd_hda_id_tegrahdmi[] = { -HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002d, "Tegra186 HDMI/DP0", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002e, "Tegra186 HDMI/DP1", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de002f, "Tegra194 HDMI/DP2", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0030, "Tegra194 HDMI/DP3", patch_tegra_hdmi), -HDA_CODEC_ENTRY(0x10de0031, "Tegra234 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0033, "SoC 33 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0034, "Tegra264 HDMI/DP", patch_tegra234_hdmi), -HDA_CODEC_ENTRY(0x10de0035, "SoC 35 HDMI/DP", patch_tegra234_hdmi), -{} /* terminator */ + HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3", MODEL_TEGRA), + HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP", MODEL_TEGRA234), + HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP", MODEL_TEGRA234), + {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi); @@ -308,6 +310,7 @@ MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI"); static struct hda_codec_driver tegrahdmi_driver = { .id = snd_hda_id_tegrahdmi, + .ops = &tegrahdmi_codec_ops, }; module_hda_codec_driver(tegrahdmi_driver); -- GitLab From cabaf5908e586156d1d59f3fb8c51d2b169bc636 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:27 +0200 Subject: [PATCH 655/868] ALSA: hda: Drop old codec binding method Now that all patch_ops usage have been converted to the new hda_codec_ops probe, we can drop patch_ops from the hda_codec, together with the calls of patch_ops callbacks. The hda_codec_ops.free callback is removed as all have been replaced with the new remove callback. Also, correct comments mentioning "patch"; it's replaced with "codec driver". Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-25-tiwai@suse.de --- include/sound/hda_codec.h | 22 ++------------------- sound/hda/Makefile | 2 +- sound/hda/codecs/generic.c | 12 ------------ sound/hda/codecs/generic.h | 1 - sound/hda/codecs/hdmi/hdmi_local.h | 2 +- sound/hda/common/bind.c | 26 +++++++------------------ sound/hda/common/codec.c | 20 ++----------------- sound/hda/common/hda_local.h | 2 -- sound/soc/codecs/hda.c | 20 ++++--------------- sound/soc/codecs/hdac_hda.c | 31 +++++++++--------------------- 10 files changed, 26 insertions(+), 112 deletions(-) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index a725ac48c20c4..ddc9c392f93f6 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -70,12 +70,8 @@ struct hda_bus { /* * codec preset - * - * Known codecs have the patch to build and set up the controls/PCMs - * better than the generic parser. */ -typedef int (*hda_codec_patch_t)(struct hda_codec *); - + #define HDA_CODEC_ID_SKIP_PROBE 0x00000001 #define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 #define HDA_CODEC_ID_GENERIC 0x00000201 @@ -90,14 +86,6 @@ typedef int (*hda_codec_patch_t)(struct hda_codec *); #define HDA_CODEC_ID(_vid, _name) \ HDA_CODEC_ID_REV(_vid, 0, _name) -/* old macros for patch_ops -- to be deprecated */ -#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \ - { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \ - .api_version = HDA_DEV_LEGACY, \ - .driver_data = (unsigned long)(_patch) } -#define HDA_CODEC_ENTRY(_vid, _name, _patch) \ - HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch) - struct hda_codec_driver { struct hdac_driver core; const struct hda_device_id *id; @@ -116,14 +104,13 @@ void hda_codec_driver_unregister(struct hda_codec_driver *drv); module_driver(drv, hda_codec_driver_register, \ hda_codec_driver_unregister) -/* ops set by the preset patch */ +/* ops for hda codec driver */ struct hda_codec_ops { int (*probe)(struct hda_codec *codec, const struct hda_device_id *id); void (*remove)(struct hda_codec *codec); int (*build_controls)(struct hda_codec *codec); int (*build_pcms)(struct hda_codec *codec); int (*init)(struct hda_codec *codec); - void (*free)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); @@ -199,9 +186,6 @@ struct hda_codec { const struct hda_device_id *preset; const char *modelname; /* model name for preset */ - /* set by patch */ - struct hda_codec_ops patch_ops; - /* PCM to create, set by hda_codec_ops.build_pcms callback */ struct list_head pcm_list_head; refcount_t pcm_ref; @@ -500,8 +484,6 @@ int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) if (driver->ops && driver->ops->check_power_status) return driver->ops->check_power_status(codec, nid); - else if (codec->patch_ops.check_power_status) - return codec->patch_ops.check_power_status(codec, nid); return 0; } diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 31b9fbedaa77e..d9a6def582efd 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -3,6 +3,6 @@ obj-y += core/ obj-$(CONFIG_SND_HDA) += common/ obj-$(CONFIG_SND_HDA) += codecs/ # this must be the last entry after codec drivers; -# otherwise the codec patches won't be hooked before the PCI probe +# otherwise the codec drivers won't be hooked before the PCI probe # when built in kernel obj-$(CONFIG_SND_HDA) += controllers/ diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 82c31b93424cf..044f1d0aeaea9 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -5190,8 +5190,6 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (spec->power_down_unused || codec->power_save_node) { if (!codec->power_filter) codec->power_filter = snd_hda_gen_path_power_filter; - if (!codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; } if (!spec->no_analog && spec->beep_nid) { @@ -6103,15 +6101,6 @@ EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status); * the generic codec support */ -static const struct hda_codec_ops generic_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, - .check_power_status = snd_hda_gen_check_power_status, -}; - static int snd_hda_gen_probe(struct hda_codec *codec, const struct hda_device_id *id) { @@ -6132,7 +6121,6 @@ static int snd_hda_gen_probe(struct hda_codec *codec, if (err < 0) goto error; - codec->patch_ops = generic_patch_ops; return 0; error: diff --git a/sound/hda/codecs/generic.h b/sound/hda/codecs/generic.h index 00a92fc558462..524591821f8c9 100644 --- a/sound/hda/codecs/generic.h +++ b/sound/hda/codecs/generic.h @@ -312,7 +312,6 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec); int snd_hda_gen_init(struct hda_codec *codec); void snd_hda_gen_remove(struct hda_codec *codec); -#define snd_hda_gen_free snd_hda_gen_remove int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h index 0654013f1fdac..548241ad3fa99 100644 --- a/sound/hda/codecs/hdmi/hdmi_local.h +++ b/sound/hda/codecs/hdmi/hdmi_local.h @@ -57,7 +57,7 @@ struct hdmi_spec_per_pin { #endif }; -/* operations used by generic code that can be overridden by patches */ +/* operations used by generic code that can be overridden by codec drivers */ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, int dev_id, unsigned char *buf, int *eld_size); diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c index 56975178f5339..f85c640dd54fa 100644 --- a/sound/hda/common/bind.c +++ b/sound/hda/common/bind.c @@ -54,8 +54,6 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) if (driver->ops && driver->ops->unsol_event) driver->ops->unsol_event(codec, ev); - else if (codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, ev); } /** @@ -91,7 +89,6 @@ static int hda_codec_driver_probe(struct device *dev) struct hda_codec *codec = dev_to_hda_codec(dev); struct module *owner = dev->driver->owner; struct hda_codec_driver *driver = hda_codec_to_driver(codec); - hda_codec_patch_t patch; int err; if (codec->bus->core.ext_ops) { @@ -115,19 +112,14 @@ static int hda_codec_driver_probe(struct device *dev) goto error; } - if (driver->ops && driver->ops->probe) { - err = driver->ops->probe(codec, codec->preset); - if (err < 0) - goto error_module_put; - } else { - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (patch) { - err = patch(codec); - if (err < 0) - goto error_module_put; - } + if (WARN_ON(!(driver->ops && driver->ops->probe))) { + err = -EINVAL; + goto error_module_put; } + err = driver->ops->probe(codec, codec->preset); + if (err < 0) + goto error_module_put; err = snd_hda_codec_build_pcms(codec); if (err < 0) goto error_module; @@ -148,8 +140,6 @@ static int hda_codec_driver_probe(struct device *dev) error_module: if (driver->ops && driver->ops->remove) driver->ops->remove(codec); - else if (codec->patch_ops.free) - codec->patch_ops.free(codec); error_module_put: module_put(owner); @@ -178,8 +168,6 @@ static int hda_codec_driver_remove(struct device *dev) if (driver->ops && driver->ops->remove) driver->ops->remove(codec); - else if (codec->patch_ops.free) - codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); codec->preset = NULL; module_put(dev->driver->owner); @@ -320,7 +308,7 @@ static int codec_bind_generic(struct hda_codec *codec) * @codec: the HDA codec * * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. + * codec driver binding. * * Returns 0 if successful or a negative error code. */ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 8899be764d689..33121d0020871 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -766,7 +766,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) snd_hda_ctls_clear(codec); codec_release_pcms(codec); snd_hda_detach_beep_device(codec); - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; @@ -1132,8 +1131,6 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (driver->ops && driver->ops->stream_pm) driver->ops->stream_pm(codec, nid, true); - else if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); @@ -1205,8 +1202,6 @@ static void really_cleanup_stream(struct hda_codec *codec, q->nid = nid; if (driver->ops && driver->ops->stream_pm) driver->ops->stream_pm(codec, nid, false); - else if (codec->patch_ops.stream_pm) - codec->patch_ops.stream_pm(codec, nid, false); } /* clean up the all conflicting obsolete streams */ @@ -2397,7 +2392,7 @@ static const struct snd_kcontrol_new dig_mixes[] = { * @cvt_nid: converter NID * @type: HDA_PCM_TYPE_* * Creates controls related with the digital output. - * Called from each patch supporting the digital out. + * Called from each codec driver supporting the digital out. * * Returns 0 if successful, or a negative error code. */ @@ -2656,7 +2651,7 @@ static const struct snd_kcontrol_new dig_in_ctls[] = { * @nid: audio in widget NID * * Creates controls related with the SPDIF input. - * Called from each patch supporting the SPDIF in. + * Called from each codec driver supporting the SPDIF in. * * Returns 0 if successful, or a negative error code. */ @@ -2773,9 +2768,6 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* might be called before binding to driver, too */ if (driver && driver->ops && driver->ops->set_power_state) driver->ops->set_power_state(codec, fg, power_state); - else if (codec->patch_ops.set_power_state) - codec->patch_ops.set_power_state(codec, fg, - power_state); else { state = power_state; if (codec->power_filter) @@ -2859,8 +2851,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) snd_hdac_enter_pm(&codec->core); if (driver->ops && driver->ops->suspend) driver->ops->suspend(codec); - else if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec); if (!codec->no_stream_clean_at_suspend) hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); @@ -2888,8 +2878,6 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_jack_set_dirty_all(codec); if (driver->ops && driver->ops->resume) driver->ops->resume(codec); - else if (codec->patch_ops.resume) - codec->patch_ops.resume(codec); else { snd_hda_codec_init(codec); snd_hda_regmap_sync(codec); @@ -3085,8 +3073,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) if (!err) { if (driver->ops && driver->ops->build_controls) err = driver->ops->build_controls(codec); - else if (codec->patch_ops.build_controls) - err = codec->patch_ops.build_controls(codec); if (err < 0) return err; } @@ -3284,8 +3270,6 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) if (driver->ops && driver->ops->build_pcms) err = driver->ops->build_pcms(codec); - else if (codec->patch_ops.build_pcms) - err = codec->patch_ops.build_pcms(codec); else return 0; diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index 654fe1156d568..e56bea4c93571 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -658,8 +658,6 @@ static inline int snd_hda_codec_init(struct hda_codec *codec) if (driver->ops && driver->ops->init) return driver->ops->init(codec); - else if (codec->patch_ops.init) - return codec->patch_ops.init(codec); return 0; } diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index ddb31001657ea..126270ffd418d 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -177,7 +177,6 @@ static int hda_codec_probe(struct snd_soc_component *component) struct hdac_device *hdev = &codec->core; struct hdac_bus *bus = hdev->bus; struct hdac_ext_link *hlink; - hda_codec_patch_t patch; int ret; #ifdef CONFIG_PM @@ -215,19 +214,12 @@ static int hda_codec_probe(struct snd_soc_component *component) goto err; } - if (driver->ops && driver->ops->probe) { - ret = driver->ops->probe(codec, codec->preset); - } else { - patch = (hda_codec_patch_t)codec->preset->driver_data; - if (!patch) { - dev_err(&hdev->dev, "no patch specified\n"); - ret = -EINVAL; - goto err; - } - - ret = patch(codec); + if (WARN_ON(!(driver->ops && driver->ops->probe))) { + ret = -EINVAL; + goto err; } + ret = driver->ops->probe(codec, codec->preset); if (ret < 0) { dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; @@ -260,8 +252,6 @@ complete_err: parse_pcms_err: if (driver->ops && driver->ops->remove) driver->ops->remove(codec); - else if (codec->patch_ops.free) - codec->patch_ops.free(codec); err: snd_hda_codec_cleanup_for_unbind(codec); device_new_err: @@ -292,8 +282,6 @@ static void hda_codec_remove(struct snd_soc_component *component) if (driver->ops && driver->ops->remove) driver->ops->remove(codec); - else if (codec->patch_ops.free) - codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); pm_runtime_put_noidle(&hdev->dev); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 7bb7845d5e437..191cb84276641 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -411,7 +411,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) struct hda_codec *hcodec = hda_pvt->codec; struct hda_codec_driver *driver = hda_codec_to_driver(hcodec); struct hdac_ext_link *hlink; - hda_codec_patch_t patch; int ret; hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); @@ -485,23 +484,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) goto error_pm; } - if (driver->ops && driver->ops->probe) { - ret = driver->ops->probe(hcodec, hcodec->preset); - if (ret < 0) { - dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret); - goto error_regmap; - } - } else { - patch = (hda_codec_patch_t)hcodec->preset->driver_data; - if (patch) { - ret = patch(hcodec); - if (ret < 0) { - dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret); - goto error_regmap; - } - } else { - dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__); - } + if (WARN_ON(!(driver->ops && driver->ops->probe))) { + ret = -EINVAL; + goto error_regmap; + } + + ret = driver->ops->probe(hcodec, hcodec->preset); + if (ret < 0) { + dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret); + goto error_regmap; } ret = snd_hda_codec_parse_pcms(hcodec); @@ -542,8 +533,6 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) error_patch: if (driver->ops && driver->ops->remove) driver->ops->remove(hcodec); - else if (hcodec->patch_ops.free) - hcodec->patch_ops.free(hcodec); error_regmap: snd_hdac_regmap_exit(hdev); error_pm: @@ -573,8 +562,6 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) if (driver->ops && driver->ops->remove) driver->ops->remove(codec); - else if (codec->patch_ops.free) - codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); } -- GitLab From 691351de31684b98a7366b08cd278bb057b51fce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:28 +0200 Subject: [PATCH 656/868] ALSA: hda: Drop superfluous driver->ops NULL checks After all conversions, driver->ops became a must in most places (except for the codec power setup which might be called before binding to the codec driver), hence we can get rid of the superfluous driver->ops NULL checks, too. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-26-tiwai@suse.de --- sound/hda/common/bind.c | 6 +++--- sound/hda/common/codec.c | 15 +++++++-------- sound/hda/common/hda_local.h | 2 +- sound/soc/codecs/hda.c | 4 ++-- sound/soc/codecs/hdac_hda.c | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c index f85c640dd54fa..bb1090b656990 100644 --- a/sound/hda/common/bind.c +++ b/sound/hda/common/bind.c @@ -52,7 +52,7 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) if (codec->core.dev.power.power_state.event != PM_EVENT_ON) return; - if (driver->ops && driver->ops->unsol_event) + if (driver->ops->unsol_event) driver->ops->unsol_event(codec, ev); } @@ -138,7 +138,7 @@ static int hda_codec_driver_probe(struct device *dev) return 0; error_module: - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(codec); error_module_put: module_put(owner); @@ -166,7 +166,7 @@ static int hda_codec_driver_remove(struct device *dev) wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); snd_power_sync_ref(codec->bus->card); - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); codec->preset = NULL; diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 33121d0020871..fa07a296abe89 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1129,7 +1129,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (!p) return; - if (driver->ops && driver->ops->stream_pm) + if (driver->ops->stream_pm) driver->ops->stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); @@ -1200,7 +1200,7 @@ static void really_cleanup_stream(struct hda_codec *codec, ); memset(q, 0, sizeof(*q)); q->nid = nid; - if (driver->ops && driver->ops->stream_pm) + if (driver->ops->stream_pm) driver->ops->stream_pm(codec, nid, false); } @@ -2849,7 +2849,7 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) unsigned int state; snd_hdac_enter_pm(&codec->core); - if (driver->ops && driver->ops->suspend) + if (driver->ops->suspend) driver->ops->suspend(codec); if (!codec->no_stream_clean_at_suspend) hda_cleanup_all_streams(codec); @@ -2876,7 +2876,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) restore_shutup_pins(codec); hda_exec_init_verbs(codec); snd_hda_jack_set_dirty_all(codec); - if (driver->ops && driver->ops->resume) + if (driver->ops->resume) driver->ops->resume(codec); else { snd_hda_codec_init(codec); @@ -3071,7 +3071,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) /* continue to initialize... */ err = snd_hda_codec_init(codec); if (!err) { - if (driver->ops && driver->ops->build_controls) + if (driver->ops->build_controls) err = driver->ops->build_controls(codec); if (err < 0) return err; @@ -3268,11 +3268,10 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) if (!list_empty(&codec->pcm_list_head)) return 0; /* already parsed */ - if (driver->ops && driver->ops->build_pcms) - err = driver->ops->build_pcms(codec); - else + if (!driver->ops->build_pcms) return 0; + err = driver->ops->build_pcms(codec); if (err < 0) { codec_err(codec, "cannot build PCMs for #%d (error %d)\n", codec->core.addr, err); diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index e56bea4c93571..a7e53277a0fea 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -656,7 +656,7 @@ static inline int snd_hda_codec_init(struct hda_codec *codec) { struct hda_codec_driver *driver = hda_codec_to_driver(codec); - if (driver->ops && driver->ops->init) + if (driver->ops->init) return driver->ops->init(codec); return 0; } diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index 126270ffd418d..c8344e28df3d8 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -250,7 +250,7 @@ static int hda_codec_probe(struct snd_soc_component *component) complete_err: hda_codec_unregister_dais(codec, component); parse_pcms_err: - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(codec); err: snd_hda_codec_cleanup_for_unbind(codec); @@ -280,7 +280,7 @@ static void hda_codec_remove(struct snd_soc_component *component) hda_codec_unregister_dais(codec, component); - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 191cb84276641..afd8edf10fdc8 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -531,7 +531,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) return 0; error_patch: - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(hcodec); error_regmap: snd_hdac_regmap_exit(hdev); @@ -560,7 +560,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); - if (driver->ops && driver->ops->remove) + if (driver->ops->remove) driver->ops->remove(codec); snd_hda_codec_cleanup_for_unbind(codec); -- GitLab From 0c4eebafea5fefef264f2f8f1c88bbe1e53efedf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:29 +0200 Subject: [PATCH 657/868] MAINTAINERS: Adjust to the new HD-audio driver paths The HD-audio drivers are moved under sound/hda now. Correct the locations. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-27-tiwai@suse.de --- MAINTAINERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5bfcaf0732ed2..bd86f2ad65b9c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5741,9 +5741,9 @@ F: drivers/spi/spi-cs42l43* F: include/dt-bindings/sound/cs* F: include/linux/mfd/cs42l43* F: include/sound/cs* -F: sound/pci/hda/cirrus* -F: sound/pci/hda/cs* -F: sound/pci/hda/hda_component* +F: sound/hda/codecs/cirrus* +F: sound/hda/codecs/side-codecs/cs* +F: sound/hda/codecs/side-codecs/hda_component* F: sound/soc/codecs/cs* CIRRUS LOGIC HAPTIC DRIVERS @@ -24533,7 +24533,7 @@ F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml F: Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml F: include/sound/tas2*.h F: include/sound/tlv320*.h -F: sound/pci/hda/tas2781_hda_i2c.c +F: sound/hda/codecs/side-codecs/tas2781_hda_i2c.c F: sound/soc/codecs/pcm1681.c F: sound/soc/codecs/pcm1789*.* F: sound/soc/codecs/pcm179x*.* @@ -27473,7 +27473,7 @@ SENARYTECH AUDIO CODEC DRIVER M: bo liu S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git -F: sound/pci/hda/patch_senarytech.c +F: sound/hda/codecs/senarytech.c THE REST M: Linus Torvalds -- GitLab From 0c8e393941d25ea13a659f184c6dc76194a473b5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Jul 2025 18:04:30 +0200 Subject: [PATCH 658/868] ALSA: hda: Return the codec init error properly at snd_hda_codec_build_controls() The error from snd_hda_codec_init() was ignored in snd_hda_codec_build_controls(), which should have been taken account and abort the flow. Fix it now. Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20250709160434.1859-28-tiwai@suse.de --- sound/hda/common/codec.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index fa07a296abe89..8e47769ef0cee 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -3065,14 +3065,16 @@ EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps); int snd_hda_codec_build_controls(struct hda_codec *codec) { struct hda_codec_driver *driver = hda_codec_to_driver(codec); - int err = 0; + int err; hda_exec_init_verbs(codec); /* continue to initialize... */ err = snd_hda_codec_init(codec); - if (!err) { - if (driver->ops->build_controls) - err = driver->ops->build_controls(codec); + if (err < 0) + return err; + + if (driver->ops->build_controls) { + err = driver->ops->build_controls(codec); if (err < 0) return err; } -- GitLab From e65cb011349e653ded541dddd6469c2ca813edcf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jul 2025 20:00:23 +0300 Subject: [PATCH 659/868] Documentation: ACPI: Fix parent device references The _CRS resources in many cases want to have ResourceSource field to be a type of ACPI String. This means that to compile properly we need to enclosure the name path into double quotes. This will in practice defer the interpretation to a run-time stage, However, this may be interpreted differently on different OSes and ACPI interpreter implementations. In particular ACPICA might not correctly recognize the leading '^' (caret) character and will not resolve the relative name path properly. On top of that, this piece may be used in SSDTs which are loaded after the DSDT and on itself may also not resolve relative name paths outside of their own scopes. With this all said, fix documentation to use fully-qualified name paths always to avoid any misinterpretations, which is proven to work. Fixes: 8eb5c87a92c0 ("i2c: add ACPI support for I2C mux ports") Reported-by: Yevhen Kondrashyn Cc: All applicable Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250710170225.961303-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- Documentation/firmware-guide/acpi/i2c-muxes.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/firmware-guide/acpi/i2c-muxes.rst b/Documentation/firmware-guide/acpi/i2c-muxes.rst index 3a8997ccd7c4b..f366539acd792 100644 --- a/Documentation/firmware-guide/acpi/i2c-muxes.rst +++ b/Documentation/firmware-guide/acpi/i2c-muxes.rst @@ -14,7 +14,7 @@ Consider this topology:: | | | 0x70 |--CH01--> i2c client B (0x50) +------+ +------+ -which corresponds to the following ASL:: +which corresponds to the following ASL (in the scope of \_SB):: Device (SMB1) { @@ -24,7 +24,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x70, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^SMB1", 0x00, + AddressingMode7Bit, "\\_SB.SMB1", 0x00, ResourceConsumer,,) } @@ -37,7 +37,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^CH00", 0x00, + AddressingMode7Bit, "\\_SB.SMB1.CH00", 0x00, ResourceConsumer,,) } } @@ -52,7 +52,7 @@ which corresponds to the following ASL:: Name (_HID, ...) Name (_CRS, ResourceTemplate () { I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED, - AddressingMode7Bit, "^CH01", 0x00, + AddressingMode7Bit, "\\_SB.SMB1.CH01", 0x00, ResourceConsumer,,) } } -- GitLab From 328d48cc0a644bc54c2ab27ee584f3510ae8c6ec Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2025 20:43:59 +0200 Subject: [PATCH 660/868] gpio: rcar: Convert to DEFINE_SIMPLE_DEV_PM_OPS() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the Renesas R-Car GPIO driver from SIMPLE_DEV_PM_OPS() to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr(). This lets us drop the check for CONFIG_PM_SLEEP, and reduces kernel size in case CONFIG_PM or CONFIG_PM_SLEEP is disabled, while increasing build coverage. Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/e201140426daacaa799d73e2f76bfd96b6f5718f.1752086619.git.geert+renesas@glider.be Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 1d121a4275905..cd31580effa90 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -592,7 +592,6 @@ static void gpio_rcar_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int gpio_rcar_suspend(struct device *dev) { struct gpio_rcar_priv *p = dev_get_drvdata(dev); @@ -651,16 +650,16 @@ static int gpio_rcar_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP*/ -static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, + gpio_rcar_resume); static struct platform_driver gpio_rcar_device_driver = { .probe = gpio_rcar_probe, .remove = gpio_rcar_remove, .driver = { .name = "gpio_rcar", - .pm = &gpio_rcar_pm_ops, + .pm = pm_sleep_ptr(&gpio_rcar_pm_ops), .of_match_table = gpio_rcar_of_table, } }; -- GitLab From 0bdbce25855f021187d16d4ffbb92102b9f13788 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 Jul 2025 10:30:50 +0200 Subject: [PATCH 661/868] ALSA: hda: Use safer strscpy() instead of strcpy() Use a safer function strscpy() instead of strcpy() for copying to arrays. Only idiomatic code replacement, and no functional changes. Link: https://patch.msgid.link/20250711083051.18759-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/hda/codecs/ca0132.c | 18 +++++++++--------- sound/hda/codecs/generic.c | 4 ++-- sound/hda/codecs/realtek/realtek.c | 4 ++-- sound/hda/common/codec.c | 4 ++-- sound/hda/controllers/intel.c | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index 6ab59c336ed44..b716f721f25d6 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -5796,7 +5796,7 @@ static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5850,7 +5850,7 @@ static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, sprintf(namestr, "%s %s", ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, sfx); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5903,7 +5903,7 @@ static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; sprintf(namestr, "%s", ae5_filter_presets[uinfo->value.enumerated.item].name); - strcpy(uinfo->value.enumerated.name, namestr); + strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5952,7 +5952,7 @@ static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS; if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, in_src_str[uinfo->value.enumerated.item]); return 0; } @@ -6004,7 +6004,7 @@ static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = NUM_OF_OUTPUTS; if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, out_type_str[uinfo->value.enumerated.item]); return 0; } @@ -6055,7 +6055,7 @@ static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, speaker_channel_cfgs[uinfo->value.enumerated.item].name); return 0; } @@ -6108,7 +6108,7 @@ static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS; if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS) uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, out_svm_set_enum_str[uinfo->value.enumerated.item]); return 0; } @@ -6172,7 +6172,7 @@ static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ca0132_alt_eq_presets[uinfo->value.enumerated.item].name); return 0; } @@ -6229,7 +6229,7 @@ static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = items; if (uinfo->value.enumerated.item >= items) uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, ca0132_voicefx_presets[uinfo->value.enumerated.item].name); return 0; } diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 044f1d0aeaea9..a44beefe3e971 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -2828,7 +2828,7 @@ static int in_jack_mode_info(struct snd_kcontrol *kcontrol, snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), vref_texts); /* set the right text */ - strcpy(uinfo->value.enumerated.name, + strscpy(uinfo->value.enumerated.name, vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); return 0; } @@ -2941,7 +2941,7 @@ static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol, text = "Mic In"; } - strcpy(uinfo->value.enumerated.name, text); + strscpy(uinfo->value.enumerated.name, text); return 0; } diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c index 66b2efb9acb37..b6feccfd45a9b 100644 --- a/sound/hda/codecs/realtek/realtek.c +++ b/sound/hda/codecs/realtek/realtek.c @@ -1130,7 +1130,7 @@ void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); + strscpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs"); break; case HDA_FIXUP_ACT_BUILD: /* rename Capture controls depending on the codec */ @@ -1155,7 +1155,7 @@ void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: /* override card longname to provide a unique UCM profile */ - strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); + strscpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs"); break; case HDA_FIXUP_ACT_BUILD: /* rename Capture controls depending on the codec */ diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index 8e47769ef0cee..eb268d442201a 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -1638,7 +1638,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) id.index = idx; if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; - strcpy(id.name, name); + strscpy(id.name, name); return snd_ctl_find_id(codec->card, &id); } @@ -3508,7 +3508,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, index = uinfo->value.enumerated.item; if (index >= imux->num_items) index = imux->num_items - 1; - strcpy(uinfo->value.enumerated.name, imux->items[index].label); + strscpy(uinfo->value.enumerated.name, imux->items[index].label); return 0; } EXPORT_SYMBOL_GPL(snd_hda_input_mux_info); diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index ebfc999156f4e..32bfd92d817fa 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2024,7 +2024,7 @@ static int azx_first_init(struct azx *chip) if (azx_acquire_irq(chip, 0) < 0) return -EBUSY; - strcpy(card->driver, "HDA-Intel"); + strscpy(card->driver, "HDA-Intel"); strscpy(card->shortname, driver_short_names[chip->driver_type], sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), -- GitLab From 6382c27389c266e90b31151a79d81fa6e122d2d6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 11 Jul 2025 09:27:09 +0200 Subject: [PATCH 662/868] platform/x86/intel/pmt/discovery: fix format string warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When -Wformat-security is enabled, this new code triggers it: drivers/platform/x86/intel/pmt/discovery.c: In function 'pmt_features_discovery': drivers/platform/x86/intel/pmt/discovery.c:505:36: error: format not a string literal and no format arguments [-Werror=format-security] 505 | pmt_feature_names[feature->id]); Fixes: d9a078809356 ("platform/x86/intel/pmt: Add PMT Discovery driver") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250711072718.2748415-1-arnd@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/discovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index 1a680a042a98e..32713a194a550 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -502,7 +502,7 @@ static int pmt_features_discovery(struct pmt_features_priv *priv, } ret = kobject_init_and_add(&feature->kobj, ktype, &priv->dev->kobj, - pmt_feature_names[feature->id]); + "%s", pmt_feature_names[feature->id]); if (ret) return ret; -- GitLab From 6e38b9fcbfa3053e1b5d2806a7233078d712bd34 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Jul 2025 17:17:28 +0200 Subject: [PATCH 663/868] platform/x86: lenovo: gamezone needs "other mode" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registering the "other mode" notifier fails if that is disabled: x86_64-linux-ld: drivers/platform/x86/lenovo/wmi-gamezone.o: in function `lwmi_gz_probe': wmi-gamezone.c:(.text+0x336): undefined reference to `devm_lwmi_om_register_notifier' This could be fixed by adding a stub helper, but a Kconfig 'select' seems simpler here. Fixes: 22024ac5366f ("platform/x86: Add Lenovo Gamezone WMI Driver") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250709151734.1268435-1-arnd@kernel.org [ij: retained the other selects as wmi-gamezone is using them directly.] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index b76157b35296a..d22b774e0236f 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -252,6 +252,7 @@ config LENOVO_WMI_GAMEZONE select ACPI_PLATFORM_PROFILE select LENOVO_WMI_EVENTS select LENOVO_WMI_HELPERS + select LENOVO_WMI_TUNING help Say Y here if you have a WMI aware Lenovo Legion device and would like to use the platform-profile firmware interface to manage power usage. -- GitLab From 1bec20dfa3d81be716e7ff5a6343bdec1d29b828 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 16:13:13 +0200 Subject: [PATCH 664/868] gpiolib: don't use GPIO global numbers in debugfs output One of the users of global GPIO numbers in the kernel are the debugfs callbacks in GPIO drivers. Before converting any custom .dbg_show() callbacks in individual modules, let's first make GPIO core stop using GPIO base in debugfs output. Use hardware offsets instead. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250707141313.73169-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7b2174b102199..62694ec12a0cd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5234,8 +5234,8 @@ core_initcall(gpiolib_dev_init); static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { bool active_low, is_irq, is_out; - unsigned int gpio = gdev->base; struct gpio_desc *desc; + unsigned int gpio = 0; struct gpio_chip *gc; unsigned long flags; int value; @@ -5339,8 +5339,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) return 0; } - seq_printf(s, "%s: GPIOs %u-%u", dev_name(&gdev->dev), gdev->base, - gdev->base + gdev->ngpio - 1); + seq_printf(s, "%s: %u GPIOs", dev_name(&gdev->dev), gdev->ngpio); parent = gc->parent; if (parent) seq_printf(s, ", parent: %s/%s", -- GitLab From 3e498b3c7b96a17037b5777c56ccff33d3bfbca5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:14 +0200 Subject: [PATCH 665/868] gpio: tps65910: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-1-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65910.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 187d21580573f..3204f55394cff 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -36,18 +36,18 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset) return 0; } -static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65910_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); struct tps65910 *tps65910 = tps65910_gpio->tps65910; if (value) - regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); - else - regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, - GPIO_SET_MASK); + return regmap_set_bits(tps65910->regmap, + TPS65910_GPIO0 + offset, GPIO_SET_MASK); + + return regmap_clear_bits(tps65910->regmap, TPS65910_GPIO0 + offset, + GPIO_SET_MASK); } static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, @@ -55,9 +55,12 @@ static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset, { struct tps65910_gpio *tps65910_gpio = gpiochip_get_data(gc); struct tps65910 *tps65910 = tps65910_gpio->tps65910; + int ret; /* Set the initial value */ - tps65910_gpio_set(gc, offset, value); + ret = tps65910_gpio_set(gc, offset, value); + if (ret) + return ret; return regmap_set_bits(tps65910->regmap, TPS65910_GPIO0 + offset, GPIO_CFG_MASK); @@ -136,7 +139,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev) tps65910_gpio->gpio_chip.can_sleep = true; tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input; tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output; - tps65910_gpio->gpio_chip.set = tps65910_gpio_set; + tps65910_gpio->gpio_chip.set_rv = tps65910_gpio_set; tps65910_gpio->gpio_chip.get = tps65910_gpio_get; tps65910_gpio->gpio_chip.parent = &pdev->dev; -- GitLab From a0b2a6bbff8c26aafdecd320f38f52c341d5cafa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:15 +0200 Subject: [PATCH 666/868] gpio: tps65912: check the return value of regmap_update_bits() regmap_update_bits() can fail, check its return value like we do elsewhere in the driver. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-2-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65912.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index fab771cb6a87b..bac757c191c2e 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -49,10 +49,13 @@ static int tps65912_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value) { struct tps65912_gpio *gpio = gpiochip_get_data(gc); + int ret; /* Set the initial value */ - regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, - GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + ret = regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + if (ret) + return ret; return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, GPIO_CFG_MASK, GPIO_CFG_MASK); -- GitLab From 22cbcfe36e9724fda06ca873e20777d863445ab8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:16 +0200 Subject: [PATCH 667/868] gpio: tps65912: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-3-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65912.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index bac757c191c2e..d586ccfbfc56c 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -76,13 +76,13 @@ static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset) return 0; } -static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, - int value) +static int tps65912_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps65912_gpio *gpio = gpiochip_get_data(gc); - regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, - GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); + return regmap_update_bits(gpio->tps->regmap, TPS65912_GPIO1 + offset, + GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); } static const struct gpio_chip template_chip = { @@ -92,7 +92,7 @@ static const struct gpio_chip template_chip = { .direction_input = tps65912_gpio_direction_input, .direction_output = tps65912_gpio_direction_output, .get = tps65912_gpio_get, - .set = tps65912_gpio_set, + .set_rv = tps65912_gpio_set, .base = -1, .ngpio = 5, .can_sleep = true, -- GitLab From e41e51f07b1c8a642fed121d01da37c1c2994f89 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:17 +0200 Subject: [PATCH 668/868] gpio: tps68470: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-4-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps68470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c index 532deaddfd4e2..3b8805c854f7d 100644 --- a/drivers/gpio/gpio-tps68470.c +++ b/drivers/gpio/gpio-tps68470.c @@ -70,8 +70,8 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc, GPIO_LINE_DIRECTION_IN; } -static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); struct regmap *regmap = tps68470_gpio->tps68470_regmap; @@ -82,7 +82,8 @@ static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, offset -= TPS68470_N_REGULAR_GPIO; } - regmap_update_bits(regmap, reg, BIT(offset), value ? BIT(offset) : 0); + return regmap_update_bits(regmap, reg, BIT(offset), + value ? BIT(offset) : 0); } static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, @@ -90,9 +91,12 @@ static int tps68470_gpio_output(struct gpio_chip *gc, unsigned int offset, { struct tps68470_gpio_data *tps68470_gpio = gpiochip_get_data(gc); struct regmap *regmap = tps68470_gpio->tps68470_regmap; + int ret; /* Set the initial value */ - tps68470_gpio_set(gc, offset, value); + ret = tps68470_gpio_set(gc, offset, value); + if (ret) + return ret; /* rest are always outputs */ if (offset >= TPS68470_N_REGULAR_GPIO) @@ -138,7 +142,7 @@ static int tps68470_gpio_probe(struct platform_device *pdev) tps68470_gpio->gc.direction_output = tps68470_gpio_output; tps68470_gpio->gc.get = tps68470_gpio_get; tps68470_gpio->gc.get_direction = tps68470_gpio_get_direction; - tps68470_gpio->gc.set = tps68470_gpio_set; + tps68470_gpio->gc.set_rv = tps68470_gpio_set; tps68470_gpio->gc.can_sleep = true; tps68470_gpio->gc.names = tps68470_names; tps68470_gpio->gc.ngpio = TPS68470_N_GPIO; -- GitLab From 9ade48906b62fc7c5b999422891408a4f02c255a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:18 +0200 Subject: [PATCH 669/868] gpio: tqmx86: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Matthias Schiffer Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-5-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tqmx86.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index 18f523a15b3c0..056799ecce6a2 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -93,14 +93,16 @@ static void _tqmx86_gpio_set(struct tqmx86_gpio_data *gpio, unsigned int offset, tqmx86_gpio_write(gpio, bitmap_get_value8(gpio->output, 0), TQMX86_GPIOD); } -static void tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int tqmx86_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct tqmx86_gpio_data *gpio = gpiochip_get_data(chip); guard(raw_spinlock_irqsave)(&gpio->spinlock); _tqmx86_gpio_set(gpio, offset, value); + + return 0; } static int tqmx86_gpio_direction_input(struct gpio_chip *chip, @@ -368,7 +370,7 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) chip->direction_output = tqmx86_gpio_direction_output; chip->get_direction = tqmx86_gpio_get_direction; chip->get = tqmx86_gpio_get; - chip->set = tqmx86_gpio_set; + chip->set_rv = tqmx86_gpio_set; chip->ngpio = TQMX86_NGPIO; chip->parent = pdev->dev.parent; -- GitLab From ed8497dc6683cd285ef4335a315d398524c4af52 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:19 +0200 Subject: [PATCH 670/868] gpio: ts4900: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-6-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ts4900.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 5c806140fdf0d..35dd2d09b4d44 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -95,16 +95,16 @@ static int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(reg & priv->input_bit); } -static void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); if (value) - regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, - TS4900_GPIO_OUT); - else - regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); + return regmap_update_bits(priv->regmap, offset, + TS4900_GPIO_OUT, TS4900_GPIO_OUT); + + return regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); } static const struct regmap_config ts4900_regmap_config = { @@ -119,7 +119,7 @@ static const struct gpio_chip template_chip = { .direction_input = ts4900_gpio_direction_input, .direction_output = ts4900_gpio_direction_output, .get = ts4900_gpio_get, - .set = ts4900_gpio_set, + .set_rv = ts4900_gpio_set, .base = -1, .can_sleep = true, }; -- GitLab From 0446ce284bebe192be6e0da6e969379dc3dac587 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:20 +0200 Subject: [PATCH 671/868] gpio: twl4030: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-7-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl4030.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index c5d7825f19c18..e39e39e3ef85b 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -120,7 +120,7 @@ static u8 cached_leden; * external pullup is needed. We could also expose the integrated PWM * as a LED brightness control; we initialize it as "always on". */ -static void twl4030_led_set_value(int led, int value) +static int twl4030_led_set_value(int led, int value) { u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; @@ -132,8 +132,8 @@ static void twl4030_led_set_value(int led, int value) else cached_leden |= mask; - WARN_ON_ONCE(twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, - TWL4030_LED_LEDEN_REG)); + return twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, + TWL4030_LED_LEDEN_REG); } static int twl4030_set_gpio_direction(int gpio, int is_input) @@ -278,7 +278,7 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) mutex_lock(&priv->mutex); if (offset >= TWL4030_GPIO_MAX) { - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); + WARN_ON_ONCE(twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1)); goto out; } @@ -334,15 +334,16 @@ out: return ret; } -static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); + int ret; mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) - twl4030_set_gpio_dataout(offset, value); + ret = twl4030_set_gpio_dataout(offset, value); else - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + ret = twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); if (value) priv->out_state |= BIT(offset); @@ -350,6 +351,8 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value) priv->out_state &= ~BIT(offset); mutex_unlock(&priv->mutex); + + return ret; } static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) @@ -373,9 +376,7 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) priv->direction |= BIT(offset); mutex_unlock(&priv->mutex); - twl_set(chip, offset, value); - - return ret; + return twl_set(chip, offset, value); } static int twl_get_direction(struct gpio_chip *chip, unsigned offset) @@ -418,7 +419,7 @@ static const struct gpio_chip template_chip = { .direction_output = twl_direction_out, .get_direction = twl_get_direction, .get = twl_get, - .set = twl_set, + .set_rv = twl_set, .to_irq = twl_to_irq, .can_sleep = true, }; -- GitLab From 77ba4640cc1564f29b280040b312688b79039c4c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:21 +0200 Subject: [PATCH 672/868] gpio: twl6040: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-8-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl6040.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index b9171bf66168f..b9c0d54d12f43 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -44,7 +44,8 @@ static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, return 0; } -static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) +static int twl6040gpo_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct twl6040 *twl6040 = gpiochip_get_data(chip); int ret; @@ -52,14 +53,14 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value) ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL); if (ret < 0) - return; + return ret; if (value) gpoctl = ret | BIT(offset); else gpoctl = ret & ~BIT(offset); - twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); + return twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); } static struct gpio_chip twl6040gpo_chip = { @@ -68,7 +69,7 @@ static struct gpio_chip twl6040gpo_chip = { .get = twl6040gpo_get, .direction_output = twl6040gpo_direction_out, .get_direction = twl6040gpo_get_direction, - .set = twl6040gpo_set, + .set_rv = twl6040gpo_set, .can_sleep = true, }; -- GitLab From 79880eba2c0feed895e6d2aa8f7e5489d113d653 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:22 +0200 Subject: [PATCH 673/868] gpio: twl6040: set line value in .direction_out() It's ok for a GPIO controller to be output-only but the .direction_out() callback must also set the requested line value. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-9-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-twl6040.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index b9c0d54d12f43..b2196b62b528c 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -37,13 +37,6 @@ static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset) return GPIO_LINE_DIRECTION_OUT; } -static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, - int value) -{ - /* This only drives GPOs, and can't change direction */ - return 0; -} - static int twl6040gpo_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -63,6 +56,13 @@ static int twl6040gpo_set(struct gpio_chip *chip, unsigned int offset, return twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl); } +static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned int offset, + int value) +{ + /* This only drives GPOs, and can't change direction */ + return twl6040gpo_set(chip, offset, value); +} + static struct gpio_chip twl6040gpo_chip = { .label = "twl6040", .owner = THIS_MODULE, -- GitLab From 42fbbe31634d116a7f6bee75c0ae455bf10a7737 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:23 +0200 Subject: [PATCH 674/868] gpio: uniphier: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-10-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-uniphier.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index d738da8718f9c..8939556f42b60 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -138,14 +138,16 @@ static int uniphier_gpio_get(struct gpio_chip *chip, unsigned int offset) return uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DATA); } -static void uniphier_gpio_set(struct gpio_chip *chip, - unsigned int offset, int val) +static int uniphier_gpio_set(struct gpio_chip *chip, + unsigned int offset, int val) { uniphier_gpio_offset_write(chip, offset, UNIPHIER_GPIO_PORT_DATA, val); + + return 0; } -static void uniphier_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, unsigned long *bits) +static int uniphier_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) { unsigned long i, bank, bank_mask, bank_bits; @@ -156,6 +158,8 @@ static void uniphier_gpio_set_multiple(struct gpio_chip *chip, uniphier_gpio_bank_write(chip, bank, UNIPHIER_GPIO_PORT_DATA, bank_mask, bank_bits); } + + return 0; } static int uniphier_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) @@ -382,8 +386,8 @@ static int uniphier_gpio_probe(struct platform_device *pdev) chip->direction_input = uniphier_gpio_direction_input; chip->direction_output = uniphier_gpio_direction_output; chip->get = uniphier_gpio_get; - chip->set = uniphier_gpio_set; - chip->set_multiple = uniphier_gpio_set_multiple; + chip->set_rv = uniphier_gpio_set; + chip->set_multiple_rv = uniphier_gpio_set_multiple; chip->to_irq = uniphier_gpio_to_irq; chip->base = -1; chip->ngpio = ngpios; -- GitLab From 55e2d1eec110f1278324882714b64465e4e58ced Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:24 +0200 Subject: [PATCH 675/868] gpio: viperboard: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-11-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-viperboard.c | 126 ++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index e55d28a8a66f2..3eba77f981d3a 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -128,45 +128,50 @@ static int vprbrd_gpioa_get(struct gpio_chip *chip, return answer; } -static void vprbrd_gpioa_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int vprbrd_gpioa_set(struct gpio_chip *chip, unsigned int offset, + int value) { - int ret; + int ret = 0; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); struct vprbrd *vb = gpio->vb; struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; - if (gpio->gpioa_out & (1 << offset)) { - if (value) - gpio->gpioa_val |= (1 << offset); - else - gpio->gpioa_val &= ~(1 << offset); - - mutex_lock(&vb->lock); - - gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; - gamsg->clk = 0x00; - gamsg->offset = offset; - gamsg->t1 = 0x00; - gamsg->t2 = 0x00; - gamsg->invert = 0x00; - gamsg->pwmlevel = 0x00; - gamsg->outval = value; - gamsg->risefall = 0x00; - gamsg->answer = 0x00; - gamsg->__fill = 0x00; - - ret = usb_control_msg(vb->usb_dev, - usb_sndctrlpipe(vb->usb_dev, 0), - VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, - 0x0000, 0x0000, gamsg, - sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); - - mutex_unlock(&vb->lock); - - if (ret != sizeof(struct vprbrd_gpioa_msg)) - dev_err(chip->parent, "usb error setting pin value\n"); + if (!(gpio->gpioa_out & (1 << offset))) + return 0; + + if (value) + gpio->gpioa_val |= (1 << offset); + else + gpio->gpioa_val &= ~(1 << offset); + + mutex_lock(&vb->lock); + + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; + gamsg->clk = 0x00; + gamsg->offset = offset; + gamsg->t1 = 0x00; + gamsg->t2 = 0x00; + gamsg->invert = 0x00; + gamsg->pwmlevel = 0x00; + gamsg->outval = value; + gamsg->risefall = 0x00; + gamsg->answer = 0x00; + gamsg->__fill = 0x00; + + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gamsg, + sizeof(struct vprbrd_gpioa_msg), + VPRBRD_USB_TIMEOUT_MS); + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpioa_msg)) { + dev_err(chip->parent, "usb error setting pin value\n"); + return -EREMOTEIO; } + + return 0; } static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, @@ -304,37 +309,42 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip, return (gpio->gpiob_val >> offset) & 0x1; } -static void vprbrd_gpiob_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int vprbrd_gpiob_set(struct gpio_chip *chip, unsigned int offset, + int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); struct vprbrd *vb = gpio->vb; struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; - if (gpio->gpiob_out & (1 << offset)) { - if (value) - gpio->gpiob_val |= (1 << offset); - else - gpio->gpiob_val &= ~(1 << offset); + if (!(gpio->gpiob_out & (1 << offset))) + return 0; - mutex_lock(&vb->lock); + if (value) + gpio->gpiob_val |= (1 << offset); + else + gpio->gpiob_val &= ~(1 << offset); - gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; - gbmsg->val = cpu_to_be16(value << offset); - gbmsg->mask = cpu_to_be16(0x0001 << offset); + mutex_lock(&vb->lock); - ret = usb_control_msg(vb->usb_dev, - usb_sndctrlpipe(vb->usb_dev, 0), - VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, - 0x0000, 0x0000, gbmsg, - sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; + gbmsg->val = cpu_to_be16(value << offset); + gbmsg->mask = cpu_to_be16(0x0001 << offset); - mutex_unlock(&vb->lock); + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, + 0x0000, 0x0000, gbmsg, + sizeof(struct vprbrd_gpiob_msg), + VPRBRD_USB_TIMEOUT_MS); - if (ret != sizeof(struct vprbrd_gpiob_msg)) - dev_err(chip->parent, "usb error setting pin value\n"); + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_gpiob_msg)) { + dev_err(chip->parent, "usb error setting pin value\n"); + return -EREMOTEIO; } + + return 0; } static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, @@ -370,14 +380,14 @@ static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, mutex_lock(&vb->lock); ret = vprbrd_gpiob_setdir(vb, offset, 1); - if (ret) + if (ret) { dev_err(chip->parent, "usb error setting pin to output\n"); + return ret; + } mutex_unlock(&vb->lock); - vprbrd_gpiob_set(chip, offset, value); - - return ret; + return vprbrd_gpiob_set(chip, offset, value); } /* ----- end of gpio b chip ---------------------------------------------- */ @@ -400,7 +410,7 @@ static int vprbrd_gpio_probe(struct platform_device *pdev) vb_gpio->gpioa.base = -1; vb_gpio->gpioa.ngpio = 16; vb_gpio->gpioa.can_sleep = true; - vb_gpio->gpioa.set = vprbrd_gpioa_set; + vb_gpio->gpioa.set_rv = vprbrd_gpioa_set; vb_gpio->gpioa.get = vprbrd_gpioa_get; vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input; vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output; @@ -416,7 +426,7 @@ static int vprbrd_gpio_probe(struct platform_device *pdev) vb_gpio->gpiob.base = -1; vb_gpio->gpiob.ngpio = 16; vb_gpio->gpiob.can_sleep = true; - vb_gpio->gpiob.set = vprbrd_gpiob_set; + vb_gpio->gpiob.set_rv = vprbrd_gpiob_set; vb_gpio->gpiob.get = vprbrd_gpiob_get; vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input; vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output; -- GitLab From e502df58b5e3767c00e887744b6eff43b7fde3ea Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 7 Jul 2025 09:50:25 +0200 Subject: [PATCH 676/868] gpio: virtio: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20250707-gpiochip-set-rv-gpio-round4-v1-12-35668aaaf6d2@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-virtio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index ac39da17a29bb..92b456475d895 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -194,11 +194,12 @@ static int virtio_gpio_get(struct gpio_chip *gc, unsigned int gpio) return ret ? ret : value; } -static void virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int virtio_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct virtio_gpio *vgpio = gpiochip_get_data(gc); - virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, NULL); + return virtio_gpio_req(vgpio, VIRTIO_GPIO_MSG_SET_VALUE, gpio, value, + NULL); } /* Interrupt handling */ @@ -565,7 +566,7 @@ static int virtio_gpio_probe(struct virtio_device *vdev) vgpio->gc.direction_input = virtio_gpio_direction_input; vgpio->gc.direction_output = virtio_gpio_direction_output; vgpio->gc.get = virtio_gpio_get; - vgpio->gc.set = virtio_gpio_set; + vgpio->gc.set_rv = virtio_gpio_set; vgpio->gc.ngpio = ngpio; vgpio->gc.base = -1; /* Allocate base dynamically */ vgpio->gc.label = dev_name(dev); -- GitLab From f8e157ff2df46ddabd930815d196895976227831 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 10 Jul 2025 15:45:51 -0700 Subject: [PATCH 677/868] thermal/drivers/qcom-spmi-temp-alarm: Enable stage 2 shutdown when required Certain TEMP_ALARM GEN2 PMIC peripherals need over-temperature stage 2 automatic PMIC partial shutdown. This will ensure that in the event of reaching the hotter stage 3 over-temperature threshold, repeated faults will be avoided during the automatic PMIC hardware full shutdown. Modify the stage 2 shutdown control logic to ensure that stage 2 shutdown is enabled on all affected PMICs. Read the digital major and minor revision registers to identify these PMICs. Signed-off-by: David Collins Signed-off-by: Anjelique Melendez Link: https://lore.kernel.org/r/20250710224555.3047790-2-anjelique.melendez@oss.qualcomm.com Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 43 ++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index a81e7d6e865f9..4b91cc13ce347 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2015, 2017, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -16,6 +17,7 @@ #include "../thermal_hwmon.h" +#define QPNP_TM_REG_DIG_MINOR 0x00 #define QPNP_TM_REG_DIG_MAJOR 0x01 #define QPNP_TM_REG_TYPE 0x04 #define QPNP_TM_REG_SUBTYPE 0x05 @@ -31,7 +33,7 @@ #define STATUS_GEN2_STATE_MASK GENMASK(6, 4) #define STATUS_GEN2_STATE_SHIFT 4 -#define SHUTDOWN_CTRL1_OVERRIDE_S2 BIT(6) +#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6) #define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0) #define SHUTDOWN_CTRL1_RATE_25HZ BIT(3) @@ -78,6 +80,7 @@ struct qpnp_tm_chip { /* protects .thresh, .stage and chip registers */ struct mutex lock; bool initialized; + bool require_stage2_shutdown; struct iio_channel *adc; const long (*temp_map)[THRESH_COUNT][STAGE_COUNT]; @@ -220,13 +223,13 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, { long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1]; long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1]; - bool disable_s2_shutdown = false; + bool disable_stage2_shutdown = false; u8 reg; WARN_ON(!mutex_is_locked(&chip->lock)); /* - * Default: S2 and S3 shutdown enabled, thresholds at + * Default: Stage 2 and Stage 3 shutdown enabled, thresholds at * lowest threshold set, monitoring at 25Hz */ reg = SHUTDOWN_CTRL1_RATE_25HZ; @@ -241,12 +244,12 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, chip->thresh = THRESH_MAX - ((stage2_threshold_max - temp) / TEMP_THRESH_STEP); - disable_s2_shutdown = true; + disable_stage2_shutdown = true; } else { chip->thresh = THRESH_MAX; if (chip->adc) - disable_s2_shutdown = true; + disable_stage2_shutdown = true; else dev_warn(chip->dev, "No ADC is configured and critical temperature %d mC is above the maximum stage 2 threshold of %ld mC! Configuring stage 2 shutdown at %ld mC.\n", @@ -255,8 +258,8 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, skip: reg |= chip->thresh; - if (disable_s2_shutdown) - reg |= SHUTDOWN_CTRL1_OVERRIDE_S2; + if (disable_stage2_shutdown && !chip->require_stage2_shutdown) + reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2; return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); } @@ -350,8 +353,8 @@ static int qpnp_tm_probe(struct platform_device *pdev) { struct qpnp_tm_chip *chip; struct device_node *node; - u8 type, subtype, dig_major; - u32 res; + u8 type, subtype, dig_major, dig_minor; + u32 res, dig_revision; int ret, irq; node = pdev->dev.of_node; @@ -402,6 +405,11 @@ static int qpnp_tm_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "could not read dig_major\n"); + ret = qpnp_tm_read(chip, QPNP_TM_REG_DIG_MINOR, &dig_minor); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "could not read dig_minor\n"); + if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1 && subtype != QPNP_TM_SUBTYPE_GEN2)) { dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", @@ -415,6 +423,23 @@ static int qpnp_tm_probe(struct platform_device *pdev) else chip->temp_map = &temp_map_gen1; + if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) { + dig_revision = (dig_major << 8) | dig_minor; + /* + * Check if stage 2 automatic partial shutdown must remain + * enabled to avoid potential repeated faults upon reaching + * over-temperature stage 3. + */ + switch (dig_revision) { + case 0x0001: + case 0x0002: + case 0x0100: + case 0x0101: + chip->require_stage2_shutdown = true; + break; + } + } + /* * Register the sensor before initializing the hardware to be able to * read the trip points. get_temp() returns the default temperature -- GitLab From 703f13285a6c5d94e67e5fe2a8c15ee51e1d76ca Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Thu, 10 Jul 2025 15:45:52 -0700 Subject: [PATCH 678/868] thermal/drivers/qcom-spmi-temp-alarm: Add temp alarm data struct based on HW subtype Currently multiple if/else statements are used in functions to decipher between SPMI temp alarm Gen 1, Gen 2 and Gen 2 Rev 1 functionality. Instead refactor the driver so that SPMI temp alarm chips will have reference to a spmi_temp_alarm_data struct which defines data and function callbacks based on the HW subtype. Signed-off-by: Anjelique Melendez Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250710224555.3047790-3-anjelique.melendez@oss.qualcomm.com Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 135 +++++++++++++------- 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 4b91cc13ce347..607838162c7d0 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -4,6 +4,7 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include #include #include @@ -31,7 +32,6 @@ #define STATUS_GEN1_STAGE_MASK GENMASK(1, 0) #define STATUS_GEN2_STATE_MASK GENMASK(6, 4) -#define STATUS_GEN2_STATE_SHIFT 4 #define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6) #define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0) @@ -43,6 +43,12 @@ #define THRESH_COUNT 4 #define STAGE_COUNT 3 +enum overtemp_stage { + STAGE1 = 0, + STAGE2, + STAGE3, +}; + /* Over-temperature trip point values in mC */ static const long temp_map_gen1[THRESH_COUNT][STAGE_COUNT] = { { 105000, 125000, 145000 }, @@ -68,22 +74,29 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = { /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ #define DEFAULT_TEMP 37000 +struct qpnp_tm_chip; + +struct spmi_temp_alarm_data { + const long (*temp_map)[THRESH_COUNT][STAGE_COUNT]; + int (*get_temp_stage)(struct qpnp_tm_chip *chip); +}; + struct qpnp_tm_chip { struct regmap *map; struct device *dev; struct thermal_zone_device *tz_dev; + const struct spmi_temp_alarm_data *data; unsigned int subtype; long temp; - unsigned int thresh; unsigned int stage; unsigned int base; /* protects .thresh, .stage and chip registers */ struct mutex lock; bool initialized; bool require_stage2_shutdown; + long temp_thresh_map[STAGE_COUNT]; struct iio_channel *adc; - const long (*temp_map)[THRESH_COUNT][STAGE_COUNT]; }; /* This array maps from GEN2 alarm state to GEN1 alarm stage */ @@ -117,34 +130,48 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data) */ static long qpnp_tm_decode_temp(struct qpnp_tm_chip *chip, unsigned int stage) { - if (!chip->temp_map || chip->thresh >= THRESH_COUNT || stage == 0 || - stage > STAGE_COUNT) + if (stage == 0 || stage > STAGE_COUNT) return 0; - return (*chip->temp_map)[chip->thresh][stage - 1]; + return chip->temp_thresh_map[stage - 1]; } /** - * qpnp_tm_get_temp_stage() - return over-temperature stage + * qpnp_tm_gen1_get_temp_stage() - return over-temperature stage * @chip: Pointer to the qpnp_tm chip * - * Return: stage (GEN1) or state (GEN2) on success, or errno on failure. + * Return: stage on success, or errno on failure. */ -static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip) +static int qpnp_tm_gen1_get_temp_stage(struct qpnp_tm_chip *chip) { int ret; - u8 reg = 0; + u8 reg; ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®); if (ret < 0) return ret; - if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) - ret = reg & STATUS_GEN1_STAGE_MASK; - else - ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT; + return FIELD_GET(STATUS_GEN1_STAGE_MASK, reg); +} - return ret; +/** + * qpnp_tm_gen2_get_temp_stage() - return over-temperature stage + * @chip: Pointer to the qpnp_tm chip + * + * Return: stage on success, or errno on failure. + */ +static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip) +{ + int ret; + u8 reg; + + ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, ®); + if (ret < 0) + return ret; + + ret = FIELD_GET(STATUS_GEN2_STATE_MASK, reg); + + return alarm_state_map[ret]; } /* @@ -153,23 +180,16 @@ static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip) */ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) { - unsigned int stage, stage_new, stage_old; + unsigned int stage_new, stage_old; int ret; WARN_ON(!mutex_is_locked(&chip->lock)); - ret = qpnp_tm_get_temp_stage(chip); + ret = chip->data->get_temp_stage(chip); if (ret < 0) return ret; - stage = ret; - - if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) { - stage_new = stage; - stage_old = chip->stage; - } else { - stage_new = alarm_state_map[stage]; - stage_old = alarm_state_map[chip->stage]; - } + stage_new = ret; + stage_old = chip->stage; if (stage_new > stage_old) { /* increasing stage, use lower bound */ @@ -181,7 +201,7 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) - TEMP_STAGE_HYSTERESIS; } - chip->stage = stage; + chip->stage = stage_new; return 0; } @@ -221,10 +241,10 @@ static int qpnp_tm_get_temp(struct thermal_zone_device *tz, int *temp) static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, int temp) { - long stage2_threshold_min = (*chip->temp_map)[THRESH_MIN][1]; - long stage2_threshold_max = (*chip->temp_map)[THRESH_MAX][1]; + long stage2_threshold_min = (*chip->data->temp_map)[THRESH_MIN][STAGE2]; + long stage2_threshold_max = (*chip->data->temp_map)[THRESH_MAX][STAGE2]; bool disable_stage2_shutdown = false; - u8 reg; + u8 reg, threshold; WARN_ON(!mutex_is_locked(&chip->lock)); @@ -236,17 +256,17 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, if (temp == THERMAL_TEMP_INVALID || temp < stage2_threshold_min) { - chip->thresh = THRESH_MIN; + threshold = THRESH_MIN; goto skip; } if (temp <= stage2_threshold_max) { - chip->thresh = THRESH_MAX - + threshold = THRESH_MAX - ((stage2_threshold_max - temp) / TEMP_THRESH_STEP); disable_stage2_shutdown = true; } else { - chip->thresh = THRESH_MAX; + threshold = THRESH_MAX; if (chip->adc) disable_stage2_shutdown = true; @@ -257,7 +277,9 @@ static int qpnp_tm_update_critical_trip_temp(struct qpnp_tm_chip *chip, } skip: - reg |= chip->thresh; + memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold], + sizeof(chip->temp_thresh_map)); + reg |= threshold; if (disable_stage2_shutdown && !chip->require_stage2_shutdown) reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2; @@ -294,6 +316,21 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) return IRQ_HANDLED; } +static const struct spmi_temp_alarm_data spmi_temp_alarm_data = { + .temp_map = &temp_map_gen1, + .get_temp_stage = qpnp_tm_gen1_get_temp_stage, +}; + +static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_data = { + .temp_map = &temp_map_gen1, + .get_temp_stage = qpnp_tm_gen2_get_temp_stage, +}; + +static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = { + .temp_map = &temp_map_gen2_v1, + .get_temp_stage = qpnp_tm_gen2_get_temp_stage, +}; + /* * This function initializes the internal temp value based on only the * current thermal stage and threshold. Setup threshold control and @@ -301,10 +338,10 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) */ static int qpnp_tm_init(struct qpnp_tm_chip *chip) { - unsigned int stage; - int ret; - u8 reg = 0; int crit_temp; + u8 threshold; + int ret; + u8 reg; mutex_lock(&chip->lock); @@ -312,19 +349,19 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) if (ret < 0) goto out; - chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; + threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; + memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold], + sizeof(chip->temp_thresh_map)); + chip->temp = DEFAULT_TEMP; - ret = qpnp_tm_get_temp_stage(chip); + ret = chip->data->get_temp_stage(chip); if (ret < 0) goto out; chip->stage = ret; - stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1 - ? chip->stage : alarm_state_map[chip->stage]; - - if (stage) - chip->temp = qpnp_tm_decode_temp(chip, stage); + if (chip->stage) + chip->temp = qpnp_tm_decode_temp(chip, chip->stage); mutex_unlock(&chip->lock); @@ -418,10 +455,14 @@ static int qpnp_tm_probe(struct platform_device *pdev) } chip->subtype = subtype; - if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1) - chip->temp_map = &temp_map_gen2_v1; + if (subtype == QPNP_TM_SUBTYPE_GEN1) + chip->data = &spmi_temp_alarm_data; + else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 0) + chip->data = &spmi_temp_alarm_gen2_data; + else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1) + chip->data = &spmi_temp_alarm_gen2_rev1_data; else - chip->temp_map = &temp_map_gen1; + return -ENODEV; if (chip->subtype == QPNP_TM_SUBTYPE_GEN2) { dig_revision = (dig_major << 8) | dig_minor; -- GitLab From 1f835c6a4c844d7667ba0f8e47e685549719f0d6 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Thu, 10 Jul 2025 15:45:53 -0700 Subject: [PATCH 679/868] thermal/drivers/qcom-spmi-temp-alarm: Prepare to support additional Temp Alarm subtypes In preparation to support newer temp alarm subtypes, add the "ops", "sync_thresholds" and "configure_trip_temps" references to spmi_temp_alarm_data. This will allow for each Temp Alarm subtype to define its own thermal_zone_device_ops and properly initialize and configure thermal trip temperature. Signed-off-by: Anjelique Melendez Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20250710224555.3047790-4-anjelique.melendez@oss.qualcomm.com Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 92 ++++++++++++++------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 607838162c7d0..c8e4db585d2bf 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -77,8 +77,11 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = { struct qpnp_tm_chip; struct spmi_temp_alarm_data { + const struct thermal_zone_device_ops *ops; const long (*temp_map)[THRESH_COUNT][STAGE_COUNT]; + int (*sync_thresholds)(struct qpnp_tm_chip *chip); int (*get_temp_stage)(struct qpnp_tm_chip *chip); + int (*configure_trip_temps)(struct qpnp_tm_chip *chip); }; struct qpnp_tm_chip { @@ -316,64 +319,95 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) return IRQ_HANDLED; } +/* Read the hardware default stage threshold temperatures */ +static int qpnp_tm_sync_thresholds(struct qpnp_tm_chip *chip) +{ + u8 reg, threshold; + int ret; + + ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®); + if (ret < 0) + return ret; + + threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; + memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold], + sizeof(chip->temp_thresh_map)); + + return ret; +} + +static int qpnp_tm_configure_trip_temp(struct qpnp_tm_chip *chip) +{ + int crit_temp, ret; + + ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp); + if (ret) + crit_temp = THERMAL_TEMP_INVALID; + + mutex_lock(&chip->lock); + ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp); + mutex_unlock(&chip->lock); + + return ret; +} + static const struct spmi_temp_alarm_data spmi_temp_alarm_data = { + .ops = &qpnp_tm_sensor_ops, .temp_map = &temp_map_gen1, + .sync_thresholds = qpnp_tm_sync_thresholds, + .configure_trip_temps = qpnp_tm_configure_trip_temp, .get_temp_stage = qpnp_tm_gen1_get_temp_stage, }; static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_data = { + .ops = &qpnp_tm_sensor_ops, .temp_map = &temp_map_gen1, + .sync_thresholds = qpnp_tm_sync_thresholds, + .configure_trip_temps = qpnp_tm_configure_trip_temp, .get_temp_stage = qpnp_tm_gen2_get_temp_stage, }; static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = { + .ops = &qpnp_tm_sensor_ops, .temp_map = &temp_map_gen2_v1, + .sync_thresholds = qpnp_tm_sync_thresholds, + .configure_trip_temps = qpnp_tm_configure_trip_temp, .get_temp_stage = qpnp_tm_gen2_get_temp_stage, }; /* * This function initializes the internal temp value based on only the - * current thermal stage and threshold. Setup threshold control and - * disable shutdown override. + * current thermal stage and threshold. */ -static int qpnp_tm_init(struct qpnp_tm_chip *chip) +static int qpnp_tm_threshold_init(struct qpnp_tm_chip *chip) { - int crit_temp; - u8 threshold; int ret; - u8 reg; - mutex_lock(&chip->lock); - - ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, ®); + ret = chip->data->sync_thresholds(chip); if (ret < 0) - goto out; - - threshold = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK; - memcpy(chip->temp_thresh_map, chip->data->temp_map[threshold], - sizeof(chip->temp_thresh_map)); - - chip->temp = DEFAULT_TEMP; + return ret; ret = chip->data->get_temp_stage(chip); if (ret < 0) - goto out; + return ret; chip->stage = ret; + chip->temp = DEFAULT_TEMP; if (chip->stage) chip->temp = qpnp_tm_decode_temp(chip, chip->stage); - mutex_unlock(&chip->lock); - - ret = thermal_zone_get_crit_temp(chip->tz_dev, &crit_temp); - if (ret) - crit_temp = THERMAL_TEMP_INVALID; + return ret; +} - mutex_lock(&chip->lock); +/* This function initializes threshold control and disables shutdown override. */ +static int qpnp_tm_init(struct qpnp_tm_chip *chip) +{ + int ret; + u8 reg; - ret = qpnp_tm_update_critical_trip_temp(chip, crit_temp); + ret = chip->data->configure_trip_temps(chip); if (ret < 0) - goto out; + return ret; /* Enable the thermal alarm PMIC module in always-on mode. */ reg = ALARM_CTRL_FORCE_ENABLE; @@ -381,8 +415,6 @@ static int qpnp_tm_init(struct qpnp_tm_chip *chip) chip->initialized = true; -out: - mutex_unlock(&chip->lock); return ret; } @@ -481,13 +513,17 @@ static int qpnp_tm_probe(struct platform_device *pdev) } } + ret = qpnp_tm_threshold_init(chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "threshold init failed\n"); + /* * Register the sensor before initializing the hardware to be able to * read the trip points. get_temp() returns the default temperature * before the hardware initialization is completed. */ chip->tz_dev = devm_thermal_of_zone_register( - &pdev->dev, 0, chip, &qpnp_tm_sensor_ops); + &pdev->dev, 0, chip, chip->data->ops); if (IS_ERR(chip->tz_dev)) return dev_err_probe(&pdev->dev, PTR_ERR(chip->tz_dev), "failed to register sensor\n"); -- GitLab From 348e104715744f4c97fb3408ee91b543eedc8af1 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Thu, 10 Jul 2025 15:45:54 -0700 Subject: [PATCH 680/868] thermal/drivers/qcom-spmi-temp-alarm: Add support for GEN2 rev 2 PMIC peripherals Add support for TEMP_ALARM GEN2 PMIC peripherals with digital major revision 2. This revision utilizes individual temp DAC registers to set the threshold temperature for over-temperature stages 1 (warning), 2 (system shutdown), and 3 (emergency shutdown) instead of a single register to specify a set of thresholds. Co-developed-by: David Collins Signed-off-by: David Collins Signed-off-by: Anjelique Melendez Link: https://lore.kernel.org/r/20250710224555.3047790-5-anjelique.melendez@oss.qualcomm.com Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 138 +++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index c8e4db585d2bf..9fbfd192017da 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -26,6 +26,11 @@ #define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40 #define QPNP_TM_REG_ALARM_CTRL 0x46 +/* TEMP_DAC_STGx registers are only present for TEMP_GEN2 v2.0 */ +#define QPNP_TM_REG_TEMP_DAC_STG1 0x47 +#define QPNP_TM_REG_TEMP_DAC_STG2 0x48 +#define QPNP_TM_REG_TEMP_DAC_STG3 0x49 + #define QPNP_TM_TYPE 0x09 #define QPNP_TM_SUBTYPE_GEN1 0x08 #define QPNP_TM_SUBTYPE_GEN2 0x09 @@ -71,6 +76,25 @@ static const long temp_map_gen2_v1[THRESH_COUNT][STAGE_COUNT] = { #define TEMP_STAGE_HYSTERESIS 2000 +/* + * For TEMP_GEN2 v2.0, TEMP_DAC_STG1/2/3 registers are used to set the threshold + * for each stage independently. + * TEMP_DAC_STG* = 0 --> 80 C + * Each 8 step increase in TEMP_DAC_STG* value corresponds to 5 C (5000 mC). + */ +#define TEMP_DAC_MIN 80000 +#define TEMP_DAC_SCALE_NUM 8 +#define TEMP_DAC_SCALE_DEN 5000 + +#define TEMP_DAC_TEMP_TO_REG(temp) \ + (((temp) - TEMP_DAC_MIN) * TEMP_DAC_SCALE_NUM / TEMP_DAC_SCALE_DEN) +#define TEMP_DAC_REG_TO_TEMP(reg) \ + (TEMP_DAC_MIN + (reg) * TEMP_DAC_SCALE_DEN / TEMP_DAC_SCALE_NUM) + +static const long temp_dac_max[STAGE_COUNT] = { + 119375, 159375, 159375 +}; + /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ #define DEFAULT_TEMP 37000 @@ -93,6 +117,7 @@ struct qpnp_tm_chip { long temp; unsigned int stage; unsigned int base; + unsigned int ntrips; /* protects .thresh, .stage and chip registers */ struct mutex lock; bool initialized; @@ -310,6 +335,54 @@ static const struct thermal_zone_device_ops qpnp_tm_sensor_ops = { .set_trip_temp = qpnp_tm_set_trip_temp, }; +static int qpnp_tm_gen2_rev2_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp) +{ + int ret, temp_cfg; + u8 reg; + + WARN_ON(!mutex_is_locked(&chip->lock)); + + if (trip >= STAGE_COUNT) { + dev_err(chip->dev, "invalid TEMP_DAC trip = %d\n", trip); + return -EINVAL; + } else if (temp < TEMP_DAC_MIN || temp > temp_dac_max[trip]) { + dev_err(chip->dev, "invalid TEMP_DAC temp = %d\n", temp); + return -EINVAL; + } + + reg = TEMP_DAC_TEMP_TO_REG(temp); + temp_cfg = TEMP_DAC_REG_TO_TEMP(reg); + + ret = qpnp_tm_write(chip, QPNP_TM_REG_TEMP_DAC_STG1 + trip, reg); + if (ret < 0) { + dev_err(chip->dev, "TEMP_DAC_STG write failed, ret=%d\n", ret); + return ret; + } + + chip->temp_thresh_map[trip] = temp_cfg; + + return 0; +} + +static int qpnp_tm_gen2_rev2_set_trip_temp(struct thermal_zone_device *tz, + const struct thermal_trip *trip, int temp) +{ + unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); + struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz); + int ret; + + mutex_lock(&chip->lock); + ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, trip_index, temp); + mutex_unlock(&chip->lock); + + return ret; +} + +static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = { + .get_temp = qpnp_tm_get_temp, + .set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp, +}; + static irqreturn_t qpnp_tm_isr(int irq, void *data) { struct qpnp_tm_chip *chip = data; @@ -351,6 +424,60 @@ static int qpnp_tm_configure_trip_temp(struct qpnp_tm_chip *chip) return ret; } +/* Configure TEMP_DAC registers based on DT thermal_zone trips */ +static int qpnp_tm_gen2_rev2_configure_trip_temps_cb(struct thermal_trip *trip, void *data) +{ + struct qpnp_tm_chip *chip = data; + int ret; + + mutex_lock(&chip->lock); + trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips); + ret = qpnp_tm_gen2_rev2_set_temp_thresh(chip, chip->ntrips, trip->temperature); + chip->ntrips++; + mutex_unlock(&chip->lock); + + return ret; +} + +static int qpnp_tm_gen2_rev2_configure_trip_temps(struct qpnp_tm_chip *chip) +{ + int ret, i; + + ret = thermal_zone_for_each_trip(chip->tz_dev, + qpnp_tm_gen2_rev2_configure_trip_temps_cb, chip); + if (ret < 0) + return ret; + + /* Verify that trips are strictly increasing. */ + for (i = 1; i < STAGE_COUNT; i++) { + if (chip->temp_thresh_map[i] <= chip->temp_thresh_map[i - 1]) { + dev_err(chip->dev, "Threshold %d=%ld <= threshold %d=%ld\n", + i, chip->temp_thresh_map[i], i - 1, + chip->temp_thresh_map[i - 1]); + return -EINVAL; + } + } + + return 0; +} + +/* Read the hardware default TEMP_DAC stage threshold temperatures */ +static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip) +{ + int ret, i; + u8 reg = 0; + + for (i = 0; i < STAGE_COUNT; i++) { + ret = qpnp_tm_read(chip, QPNP_TM_REG_TEMP_DAC_STG1 + i, ®); + if (ret < 0) + return ret; + + chip->temp_thresh_map[i] = TEMP_DAC_REG_TO_TEMP(reg); + } + + return 0; +} + static const struct spmi_temp_alarm_data spmi_temp_alarm_data = { .ops = &qpnp_tm_sensor_ops, .temp_map = &temp_map_gen1, @@ -375,6 +502,13 @@ static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev1_data = { .get_temp_stage = qpnp_tm_gen2_get_temp_stage, }; +static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = { + .ops = &qpnp_tm_gen2_rev2_sensor_ops, + .sync_thresholds = qpnp_tm_gen2_rev2_sync_thresholds, + .configure_trip_temps = qpnp_tm_gen2_rev2_configure_trip_temps, + .get_temp_stage = qpnp_tm_gen2_get_temp_stage, +}; + /* * This function initializes the internal temp value based on only the * current thermal stage and threshold. @@ -491,8 +625,10 @@ static int qpnp_tm_probe(struct platform_device *pdev) chip->data = &spmi_temp_alarm_data; else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 0) chip->data = &spmi_temp_alarm_gen2_data; - else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 1) + else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major == 1) chip->data = &spmi_temp_alarm_gen2_rev1_data; + else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2) + chip->data = &spmi_temp_alarm_gen2_rev2_data; else return -ENODEV; -- GitLab From 97d4d7742d0986426cc48f58b6baae04953eae04 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Thu, 10 Jul 2025 15:45:55 -0700 Subject: [PATCH 681/868] thermal/drivers/qcom-spmi-temp-alarm: Add support for LITE PMIC peripherals Add support for TEMP_ALARM LITE PMIC peripherals. This subtype utilizes a pair of registers to configure a warning interrupt threshold temperature and an automatic hardware shutdown threshold temperature. Co-developed-by: David Collins Signed-off-by: David Collins Signed-off-by: Anjelique Melendez Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250710224555.3047790-6-anjelique.melendez@oss.qualcomm.com Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/qcom-spmi-temp-alarm.c | 208 +++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c index 9fbfd192017da..f39ca0ddd17b6 100644 --- a/drivers/thermal/qcom/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom/qcom-spmi-temp-alarm.c @@ -23,6 +23,7 @@ #define QPNP_TM_REG_TYPE 0x04 #define QPNP_TM_REG_SUBTYPE 0x05 #define QPNP_TM_REG_STATUS 0x08 +#define QPNP_TM_REG_IRQ_STATUS 0x10 #define QPNP_TM_REG_SHUTDOWN_CTRL1 0x40 #define QPNP_TM_REG_ALARM_CTRL 0x46 @@ -30,14 +31,20 @@ #define QPNP_TM_REG_TEMP_DAC_STG1 0x47 #define QPNP_TM_REG_TEMP_DAC_STG2 0x48 #define QPNP_TM_REG_TEMP_DAC_STG3 0x49 +#define QPNP_TM_REG_LITE_TEMP_CFG1 0x50 +#define QPNP_TM_REG_LITE_TEMP_CFG2 0x51 #define QPNP_TM_TYPE 0x09 #define QPNP_TM_SUBTYPE_GEN1 0x08 #define QPNP_TM_SUBTYPE_GEN2 0x09 +#define QPNP_TM_SUBTYPE_LITE 0xC0 #define STATUS_GEN1_STAGE_MASK GENMASK(1, 0) #define STATUS_GEN2_STATE_MASK GENMASK(6, 4) +/* IRQ status only needed for TEMP_ALARM_LITE */ +#define IRQ_STATUS_MASK BIT(0) + #define SHUTDOWN_CTRL1_OVERRIDE_STAGE2 BIT(6) #define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0) @@ -45,6 +52,8 @@ #define ALARM_CTRL_FORCE_ENABLE BIT(7) +#define LITE_TEMP_CFG_THRESHOLD_MASK GENMASK(3, 2) + #define THRESH_COUNT 4 #define STAGE_COUNT 3 @@ -95,6 +104,19 @@ static const long temp_dac_max[STAGE_COUNT] = { 119375, 159375, 159375 }; +/* + * TEMP_ALARM_LITE has two stages: warning and shutdown with independently + * configured threshold temperatures. + */ + +static const long temp_lite_warning_map[THRESH_COUNT] = { + 115000, 125000, 135000, 145000 +}; + +static const long temp_lite_shutdown_map[THRESH_COUNT] = { + 135000, 145000, 160000, 175000 +}; + /* Temperature in Milli Celsius reported during stage 0 if no ADC is present */ #define DEFAULT_TEMP 37000 @@ -202,6 +224,24 @@ static int qpnp_tm_gen2_get_temp_stage(struct qpnp_tm_chip *chip) return alarm_state_map[ret]; } +/** + * qpnp_tm_lite_get_temp_stage() - return over-temperature stage + * @chip: Pointer to the qpnp_tm chip + * + * Return: alarm interrupt state on success, or errno on failure. + */ +static int qpnp_tm_lite_get_temp_stage(struct qpnp_tm_chip *chip) +{ + u8 reg = 0; + int ret; + + ret = qpnp_tm_read(chip, QPNP_TM_REG_IRQ_STATUS, ®); + if (ret < 0) + return ret; + + return FIELD_GET(IRQ_STATUS_MASK, reg); +} + /* * This function updates the internal temp value based on the * current thermal stage and threshold as well as the previous stage @@ -383,6 +423,98 @@ static const struct thermal_zone_device_ops qpnp_tm_gen2_rev2_sensor_ops = { .set_trip_temp = qpnp_tm_gen2_rev2_set_trip_temp, }; +static int qpnp_tm_lite_set_temp_thresh(struct qpnp_tm_chip *chip, unsigned int trip, int temp) +{ + int ret, temp_cfg, i; + const long *temp_map; + u8 reg, thresh; + u16 addr; + + WARN_ON(!mutex_is_locked(&chip->lock)); + + if (trip >= STAGE_COUNT) { + dev_err(chip->dev, "invalid TEMP_LITE trip = %d\n", trip); + return -EINVAL; + } + + switch (trip) { + case 0: + temp_map = temp_lite_warning_map; + addr = QPNP_TM_REG_LITE_TEMP_CFG1; + break; + case 1: + /* + * The second trip point is purely in software to facilitate + * a controlled shutdown after the warning threshold is crossed + * but before the automatic hardware shutdown threshold is + * crossed. + */ + return 0; + case 2: + temp_map = temp_lite_shutdown_map; + addr = QPNP_TM_REG_LITE_TEMP_CFG2; + break; + default: + return 0; + } + + if (temp < temp_map[THRESH_MIN] || temp > temp_map[THRESH_MAX]) { + dev_err(chip->dev, "invalid TEMP_LITE temp = %d\n", temp); + return -EINVAL; + } + + thresh = 0; + temp_cfg = temp_map[thresh]; + for (i = THRESH_MAX; i >= THRESH_MIN; i--) { + if (temp >= temp_map[i]) { + thresh = i; + temp_cfg = temp_map[i]; + break; + } + } + + if (temp_cfg == chip->temp_thresh_map[trip]) + return 0; + + ret = qpnp_tm_read(chip, addr, ®); + if (ret < 0) { + dev_err(chip->dev, "LITE_TEMP_CFG read failed, ret=%d\n", ret); + return ret; + } + + reg &= ~LITE_TEMP_CFG_THRESHOLD_MASK; + reg |= FIELD_PREP(LITE_TEMP_CFG_THRESHOLD_MASK, thresh); + + ret = qpnp_tm_write(chip, addr, reg); + if (ret < 0) { + dev_err(chip->dev, "LITE_TEMP_CFG write failed, ret=%d\n", ret); + return ret; + } + + chip->temp_thresh_map[trip] = temp_cfg; + + return 0; +} + +static int qpnp_tm_lite_set_trip_temp(struct thermal_zone_device *tz, + const struct thermal_trip *trip, int temp) +{ + unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv); + struct qpnp_tm_chip *chip = thermal_zone_device_priv(tz); + int ret; + + mutex_lock(&chip->lock); + ret = qpnp_tm_lite_set_temp_thresh(chip, trip_index, temp); + mutex_unlock(&chip->lock); + + return ret; +} + +static const struct thermal_zone_device_ops qpnp_tm_lite_sensor_ops = { + .get_temp = qpnp_tm_get_temp, + .set_trip_temp = qpnp_tm_lite_set_trip_temp, +}; + static irqreturn_t qpnp_tm_isr(int irq, void *data) { struct qpnp_tm_chip *chip = data; @@ -478,6 +610,70 @@ static int qpnp_tm_gen2_rev2_sync_thresholds(struct qpnp_tm_chip *chip) return 0; } +/* Configure TEMP_LITE registers based on DT thermal_zone trips */ +static int qpnp_tm_lite_configure_trip_temps_cb(struct thermal_trip *trip, void *data) +{ + struct qpnp_tm_chip *chip = data; + int ret; + + mutex_lock(&chip->lock); + trip->priv = THERMAL_INT_TO_TRIP_PRIV(chip->ntrips); + ret = qpnp_tm_lite_set_temp_thresh(chip, chip->ntrips, trip->temperature); + chip->ntrips++; + mutex_unlock(&chip->lock); + + return ret; +} + +static int qpnp_tm_lite_configure_trip_temps(struct qpnp_tm_chip *chip) +{ + int ret; + + ret = thermal_zone_for_each_trip(chip->tz_dev, qpnp_tm_lite_configure_trip_temps_cb, chip); + if (ret < 0) + return ret; + + /* Verify that trips are strictly increasing. */ + if (chip->temp_thresh_map[2] <= chip->temp_thresh_map[0]) { + dev_err(chip->dev, "Threshold 2=%ld <= threshold 0=%ld\n", + chip->temp_thresh_map[2], chip->temp_thresh_map[0]); + return -EINVAL; + } + + return 0; +} + +/* Read the hardware default TEMP_LITE stage threshold temperatures */ +static int qpnp_tm_lite_sync_thresholds(struct qpnp_tm_chip *chip) +{ + int ret, thresh; + u8 reg = 0; + + /* + * Store the warning trip temp in temp_thresh_map[0] and the shutdown trip + * temp in temp_thresh_map[2]. The second trip point is purely in software + * to facilitate a controlled shutdown after the warning threshold is + * crossed but before the automatic hardware shutdown threshold is + * crossed. Thus, there is no register to read for the second trip + * point. + */ + ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG1, ®); + if (ret < 0) + return ret; + + thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg); + chip->temp_thresh_map[0] = temp_lite_warning_map[thresh]; + + ret = qpnp_tm_read(chip, QPNP_TM_REG_LITE_TEMP_CFG2, ®); + if (ret < 0) + return ret; + + thresh = FIELD_GET(LITE_TEMP_CFG_THRESHOLD_MASK, reg); + chip->temp_thresh_map[2] = temp_lite_shutdown_map[thresh]; + + return 0; +} + static const struct spmi_temp_alarm_data spmi_temp_alarm_data = { .ops = &qpnp_tm_sensor_ops, .temp_map = &temp_map_gen1, @@ -509,6 +705,13 @@ static const struct spmi_temp_alarm_data spmi_temp_alarm_gen2_rev2_data = { .get_temp_stage = qpnp_tm_gen2_get_temp_stage, }; +static const struct spmi_temp_alarm_data spmi_temp_alarm_lite_data = { + .ops = &qpnp_tm_lite_sensor_ops, + .sync_thresholds = qpnp_tm_lite_sync_thresholds, + .configure_trip_temps = qpnp_tm_lite_configure_trip_temps, + .get_temp_stage = qpnp_tm_lite_get_temp_stage, +}; + /* * This function initializes the internal temp value based on only the * current thermal stage and threshold. @@ -614,7 +817,8 @@ static int qpnp_tm_probe(struct platform_device *pdev) "could not read dig_minor\n"); if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1 - && subtype != QPNP_TM_SUBTYPE_GEN2)) { + && subtype != QPNP_TM_SUBTYPE_GEN2 + && subtype != QPNP_TM_SUBTYPE_LITE)) { dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", type, subtype); return -ENODEV; @@ -629,6 +833,8 @@ static int qpnp_tm_probe(struct platform_device *pdev) chip->data = &spmi_temp_alarm_gen2_rev1_data; else if (subtype == QPNP_TM_SUBTYPE_GEN2 && dig_major >= 2) chip->data = &spmi_temp_alarm_gen2_rev2_data; + else if (subtype == QPNP_TM_SUBTYPE_LITE) + chip->data = &spmi_temp_alarm_lite_data; else return -ENODEV; -- GitLab From 6894e49b7b62cdb0edbcaaa23ea0218edb3b02dd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 11 Jul 2025 11:06:15 +0100 Subject: [PATCH 682/868] ASoC: SDCA: Kconfig/Makefile fixups Tidy up a few bits of the SDCA Kconfig. Default both HID and IRQ to enabled, since typically if one wants SDCA all the functionality will be expected. Finally, update the IRQ support to match the changes made to the HID support. Signed-off-by: Charles Keepax Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20250711100616.296329-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/Kconfig | 13 +++++++++---- sound/soc/sdca/Makefile | 10 ++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig index 2253a300dcc35..6a3ba43f26bd9 100644 --- a/sound/soc/sdca/Kconfig +++ b/sound/soc/sdca/Kconfig @@ -8,19 +8,24 @@ config SND_SOC_SDCA This option enables support for the MIPI SoundWire Device Class for Audio (SDCA). -config SND_SOC_SDCA_OPTIONAL - def_tristate SND_SOC_SDCA || !SND_SOC_SDCA - config SND_SOC_SDCA_HID bool "SDCA HID support" depends on SND_SOC_SDCA depends on HID=y || HID=SND_SOC_SDCA + default y + help + This option enables support for audio jack button reporting using HID. config SND_SOC_SDCA_IRQ - tristate + bool "SDCA IRQ support" select REGMAP select REGMAP_IRQ + depends on SND_SOC_SDCA + default y help This option enables support for SDCA IRQs. +config SND_SOC_SDCA_OPTIONAL + def_tristate SND_SOC_SDCA || !SND_SOC_SDCA + endmenu diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 1efc869c6cbc3..58a8f3ef3feb2 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,11 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o +snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o snd-soc-sdca-hid-y := sdca_hid.o snd-soc-sdca-irq-y := sdca_interrupts.o -obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o ifdef CONFIG_SND_SOC_SDCA_HID -obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-hid.o +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-hid.o +endif +ifdef CONFIG_SND_SOC_SDCA_IRQ +obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-irq.o endif -obj-$(CONFIG_SND_SOC_SDCA_IRQ) += snd-soc-sdca-irq.o -- GitLab From 5030abcb0aa3304bf91497844ffa9607a2d4ad5d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 11 Jul 2025 11:06:16 +0100 Subject: [PATCH 683/868] ASoC: SDCA: Pull HID and IRQ into the primary SDCA module If the HID or the IRQ are selected as options they will always require loading alongside the main SDCA module. Since it will never be possible to run without them the value of keeping them as separate modules is fairly limited. Pull them into the main SDCA module to simplify things still further. Signed-off-by: Charles Keepax Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20250711100616.296329-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/Makefile | 10 ++-------- sound/soc/sdca/sdca_functions.c | 1 - sound/soc/sdca/sdca_hid.c | 2 +- sound/soc/sdca/sdca_interrupts.c | 8 ++++---- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index 58a8f3ef3feb2..5e51760cb6513 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,13 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o -snd-soc-sdca-hid-y := sdca_hid.o -snd-soc-sdca-irq-y := sdca_interrupts.o +snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o +snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o -ifdef CONFIG_SND_SOC_SDCA_HID -obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-hid.o -endif -ifdef CONFIG_SND_SOC_SDCA_IRQ -obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca-irq.o -endif diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index c34f3bf629833..be09b7a341027 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1943,4 +1943,3 @@ EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); -MODULE_IMPORT_NS("SND_SOC_SDCA_HID"); diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index b227ad94d08ff..2224ade59affa 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -121,7 +121,7 @@ int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity) return 0; } -EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA_HID"); +EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA HID library"); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index edb045c7ebb08..2f85fcc6e5446 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -279,7 +279,7 @@ int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, return 0; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA_IRQ"); +EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA"); /** * sdca_irq_data_populate - Populate common interrupt data @@ -313,7 +313,7 @@ int sdca_irq_data_populate(struct snd_soc_component *component, return 0; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA_IRQ"); +EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA"); /** * sdca_irq_populate - Request all the individual IRQs for an SDCA Function @@ -393,7 +393,7 @@ int sdca_irq_populate(struct sdca_function_data *function, return 0; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA_IRQ"); +EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA"); /** * sdca_irq_allocate - allocate an SDCA interrupt structure for a device @@ -433,7 +433,7 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, return info; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA_IRQ"); +EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SDCA IRQ library"); -- GitLab From f40ecc2743652c0b0f19935f81baf57c601eb7f0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Jul 2025 02:26:39 +0000 Subject: [PATCH 684/868] ASoC: soc-dapm: set bias_level if snd_soc_dapm_set_bias_level() was successed ASoC has 2 functions to set bias level. (A) snd_soc_dapm_force_bias_level() (B) snd_soc_dapm_set_bias_level() snd_soc_dapm_force_bias_level() (A) will set dapm->bias_level (a) if successed. (A) int snd_soc_dapm_force_bias_level(...) { ... if (ret == 0) (a) dapm->bias_level = level; ... } snd_soc_dapm_set_bias_level() (B) is also a function that sets bias_level. It will call snd_soc_dapm_force_bias_level() (A) inside, but doesn't set dapm->bias_level by itself. One note is that (A) might not be called. (B) static int snd_soc_dapm_set_bias_level(...) { ... ret = snd_soc_card_set_bias_level(...); ... if (dapm != &card->dapm) (A) ret = snd_soc_dapm_force_bias_level(...); ... ret = snd_soc_card_set_bias_level_post(...); ... } dapm->bias_level will be set if (A) was called, but might not be set if (B) was called, even though it calles set_bias_level() function. We should set dapm->bias_level if we calls snd_soc_dapm_set_bias_level() (B), too. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87qzyn4g4h.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b8a5875378c87..a37d44cd04c67 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1030,6 +1030,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, out: trace_snd_soc_bias_level_done(dapm, level); + /* success */ + if (ret == 0) + snd_soc_dapm_init_bias_level(dapm, level); + return ret; } -- GitLab From 88f60cb4b8c96241d86a68da57dc5b52488240cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Jul 2025 02:26:54 +0000 Subject: [PATCH 685/868] ASoC: samsung: bell: don't set dapm->bias_level directly snd_soc_dapm_set_bias_level() (A) will set dapm->bias_level (a) inside. No need to set it by each callback function. Remove it. (A) static int snd_soc_dapm_set_bias_level(...) { ... /* success */ if (ret == 0) (a) snd_soc_dapm_init_bias_level(dapm, level); ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ple74g47.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/bells.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 8dc3b2da4c8f4..61ed5e69391a4 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -133,8 +133,6 @@ static int bells_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } -- GitLab From d2f423a4f4ecabd852c5ee5076596399488d4b75 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Jul 2025 02:27:04 +0000 Subject: [PATCH 686/868] ASoC: samsung: speyside: don't set dapm->bias_level directly snd_soc_dapm_set_bias_level() (A) will set dapm->bias_level (a) inside. No need to set it by each callback function. Remove it. (A) static int snd_soc_dapm_set_bias_level(...) { ... /* success */ if (ret == 0) (a) snd_soc_dapm_init_bias_level(dapm, level); ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87o6tr4g3r.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/speyside.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 9262e56265842..f4cc5684ef0ab 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -90,8 +90,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card, break; } - card->dapm.bias_level = level; - return 0; } -- GitLab From 4421e455d2c33a86cdb19f3a0854a59de1542321 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Jul 2025 02:27:12 +0000 Subject: [PATCH 687/868] ASoC: samsung: tobermory: don't set dapm->bias_level directly snd_soc_dapm_set_bias_level() (A) will set dapm->bias_level (a) inside. No need to set it by each callback function. Remove it. (A) static int snd_soc_dapm_set_bias_level(...) { ... /* success */ if (ret == 0) (a) snd_soc_dapm_init_bias_level(dapm, level); ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ms9b4g3j.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/tobermory.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index d0f0c01365aa5..1d0a782402f03 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -91,8 +91,6 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } -- GitLab From f00e06296ba3f0d8440030afe8cc2258758b7af7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 11 Jul 2025 02:27:19 +0000 Subject: [PATCH 688/868] ASoC: samsung: littlemill: don't set dapm->bias_level directly snd_soc_dapm_set_bias_level() (A) will set dapm->bias_level (a) inside. No need to set it by each callback function. Remove it. (A) static int snd_soc_dapm_set_bias_level(...) { ... /* success */ if (ret == 0) (a) snd_soc_dapm_init_bias_level(dapm, level); ... } Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87ldov4g3c.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/samsung/littlemill.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 5a02aac9b423d..c8b06894ac88c 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -95,8 +95,6 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card, break; } - dapm->bias_level = level; - return 0; } -- GitLab From 69d5b62c4bded309332add0fac6760239ff47a68 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 10 Jul 2025 20:40:01 +0800 Subject: [PATCH 689/868] ASoC: codec: tlv320aic32x4: Drop aic32x4_pdata usage There is no machine is using aic32x4_pdata as platform_data, so remove the dead code. Cc: Markus Niebel Cc: Alexander Stein Reviewed-by: Alexander Stein Signed-off-by: Peng Fan Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20250710-asoc-gpio-1-v2-1-2233b272a1a6@nxp.com Signed-off-by: Mark Brown --- include/sound/tlv320aic32x4.h | 9 --------- sound/soc/codecs/tlv320aic32x4.c | 9 +-------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h index 0abf74d7edbd6..b779d671a9957 100644 --- a/include/sound/tlv320aic32x4.h +++ b/include/sound/tlv320aic32x4.h @@ -40,13 +40,4 @@ struct aic32x4_setup_data { unsigned int gpio_func[5]; }; - -struct aic32x4_pdata { - struct aic32x4_setup_data *setup; - u32 power_cfg; - u32 micpga_routing; - bool swapdacs; - int rstn_gpio; -}; - #endif diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 54ea4bc58c276..7dbcf7f7130b0 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1346,7 +1346,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, enum aic32x4_type type) { struct aic32x4_priv *aic32x4; - struct aic32x4_pdata *pdata = dev->platform_data; struct device_node *np = dev->of_node; int ret; @@ -1363,13 +1362,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, aic32x4); - if (pdata) { - aic32x4->power_cfg = pdata->power_cfg; - aic32x4->swapdacs = pdata->swapdacs; - aic32x4->micpga_routing = pdata->micpga_routing; - aic32x4->rstn_gpio = pdata->rstn_gpio; - aic32x4->mclk_name = "mclk"; - } else if (np) { + if (np) { ret = aic32x4_parse_dt(aic32x4, np); if (ret) { dev_err(dev, "Failed to parse DT node\n"); -- GitLab From b709c1aef5e15db3aff5749fc7ed9c61b8d0a322 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 10 Jul 2025 20:40:02 +0800 Subject: [PATCH 690/868] ASoC: codec: tlv320aic32x4: Sort headers alphabetically Sort headers alphabetically to easily insert new ones and drop unused ones. Signed-off-by: Peng Fan Reviewed-by: Alexander Stein Link: https://patch.msgid.link/20250710-asoc-gpio-1-v2-2-2233b272a1a6@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 7dbcf7f7130b0..2f4147387c4f8 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -9,27 +9,27 @@ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. */ -#include -#include -#include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include #include +#include +#include #include +#include -#include #include +#include #include #include #include #include -#include #include +#include #include "tlv320aic32x4.h" -- GitLab From 790d5f8ee6f2a27686d042abbce16b4e03ac1608 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 10 Jul 2025 20:40:03 +0800 Subject: [PATCH 691/868] ASoC: codec: tlv320aic32x4: Convert to GPIO descriptors of_gpio.h is deprecated, update the driver to use GPIO descriptors. - Use devm_gpiod_get_optional to get GPIO descriptor, and set consumer name. - Use gpiod_set_value to configure output value. While at here, reorder the included headers. Checking the DTS that use the device, all are using GPIOD_ACTIVE_LOW polarity for reset-gpios, so all should work as expected with this patch. Cc: Markus Niebel Cc: Alexander Stein Reviewed-by: Linus Walleij Tested-by: Alexander Stein Signed-off-by: Peng Fan Link: https://patch.msgid.link/20250710-asoc-gpio-1-v2-3-2233b272a1a6@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 2f4147387c4f8..3b89980e9bcf2 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -12,12 +12,11 @@ #include #include #include -#include +#include #include #include #include #include -#include #include #include #include @@ -38,7 +37,7 @@ struct aic32x4_priv { u32 power_cfg; u32 micpga_routing; bool swapdacs; - int rstn_gpio; + struct gpio_desc *rstn_gpio; const char *mclk_name; struct regulator *supply_ldo; @@ -1236,7 +1235,14 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; - aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */ + aic32x4->rstn_gpio = devm_gpiod_get_optional(aic32x4->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(aic32x4->rstn_gpio)) { + return dev_err_probe(aic32x4->dev, PTR_ERR(aic32x4->rstn_gpio), + "Failed to get reset gpio\n"); + } else { + gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn"); + } if (of_property_read_u32_array(np, "aic32x4-gpio-func", aic32x4_setup->gpio_func, 5) >= 0) @@ -1372,26 +1378,20 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, aic32x4->power_cfg = 0; aic32x4->swapdacs = false; aic32x4->micpga_routing = 0; - aic32x4->rstn_gpio = -1; + aic32x4->rstn_gpio = NULL; aic32x4->mclk_name = "mclk"; } - if (gpio_is_valid(aic32x4->rstn_gpio)) { - ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, - GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); - if (ret != 0) - return ret; - } - ret = aic32x4_setup_regulators(dev, aic32x4); if (ret) { dev_err(dev, "Failed to setup regulators\n"); return ret; } - if (gpio_is_valid(aic32x4->rstn_gpio)) { + if (!aic32x4->rstn_gpio) { ndelay(10); - gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); + /* deassert reset */ + gpiod_set_value_cansleep(aic32x4->rstn_gpio, 0); mdelay(1); } -- GitLab From af241e3fa4d823f8af899c92fd50d020816a1860 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 8 Jul 2025 16:53:17 +0800 Subject: [PATCH 692/868] ASoC: fsl-asoc-card: add sysclk_ratio for calculate sysclk frequency The sysclk frequency can be calculated from sample rate multiply ratio. When sysclk_freq is not configured, but sysclk_ratio is configured, then calculate sysclk frequency according to sysclk_ratio. Apply this change for wm8524 codec. Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20250708085318.2563521-1-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index ab583b432c60b..71113886e494b 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -63,6 +63,7 @@ struct codec_priv { * @sysclk_freq: SYSCLK rates for set_sysclk() * @sysclk_dir: SYSCLK directions for set_sysclk() * @sysclk_id: SYSCLK ids for set_sysclk() + * @sysclk_ratio: SYSCLK ratio on sample rate * @slot_width: Slot width of each frame * @slot_num: Number of slots of each frame * @@ -72,6 +73,7 @@ struct cpu_priv { unsigned long sysclk_freq[2]; u32 sysclk_dir[2]; u32 sysclk_id[2]; + u32 sysclk_ratio[2]; u32 slot_width; u32 slot_num; }; @@ -176,7 +178,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai; struct cpu_priv *cpu_priv = &priv->cpu_priv; struct device *dev = rtd->card->dev; - unsigned int pll_out; + unsigned int pll_out, sysclk_freq; int codec_idx; int ret; @@ -187,9 +189,14 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, if (fsl_asoc_card_is_ac97(priv)) return 0; + if (!cpu_priv->sysclk_freq[tx] && cpu_priv->sysclk_ratio[tx]) + sysclk_freq = priv->sample_rate * cpu_priv->sysclk_ratio[tx]; + else + sysclk_freq = cpu_priv->sysclk_freq[tx]; + /* Specific configurations of DAIs starts from here */ ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), cpu_priv->sysclk_id[tx], - cpu_priv->sysclk_freq[tx], + sysclk_freq, cpu_priv->sysclk_dir[tx]); if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk for cpu dai\n"); @@ -799,6 +806,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.slot_width = 32; priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_ratio[TX] = 256; } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { codec_dai_name[0] = "si476x-codec"; priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; -- GitLab From 729ff4a936c6f3faba78aaa8bc4291b6477c6576 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:39 +0200 Subject: [PATCH 693/868] regulator: dt-bindings: qcom,rpmh: Add PM7550 compatible Add the PM7550 compatible for the regulators in the PMIC found with the Milos SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-1-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- .../regulator/qcom,rpmh-regulator.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3a5a0a6cf5cc7..3dd150e5dad89 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -40,6 +40,7 @@ description: | For PM660, smps1 - smps6, ldo1 - ldo3, ldo5 - ldo19 For PM660L, smps1 - smps3, smps5, ldo1 - ldo8, bob For PM7325, smps1 - smps8, ldo1 - ldo19 + For PM7550, smps1 - smps6, ldo1 - ldo23, bob For PM8005, smps1 - smps4 For PM8009, smps1 - smps2, ldo1 - ldo7 For PM8010, ldo1 - ldo7 @@ -66,6 +67,7 @@ properties: - qcom,pm660-rpmh-regulators - qcom,pm660l-rpmh-regulators - qcom,pm7325-rpmh-regulators + - qcom,pm7550-rpmh-regulators - qcom,pm8005-rpmh-regulators - qcom,pm8009-rpmh-regulators - qcom,pm8009-1-rpmh-regulators @@ -218,6 +220,25 @@ allOf: "^vdd-l[358]-supply$": true "^vdd-s[1-8]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pm7550-rpmh-regulators + then: + properties: + vdd-bob-supply: + description: BOB regulator parent supply phandle. + vdd-l2-l3-supply: true + vdd-l4-l5-supply: true + vdd-l9-l10-supply: true + vdd-l12-l14-supply: true + vdd-l13-l16-supply: true + vdd-l15-l17-l18-l19-l20-l21-l22-l23-supply: true + patternProperties: + "^vdd-l(1|[6-8]|11)-supply$": true + "^vdd-s[1-6]-supply$": true + - if: properties: compatible: -- GitLab From 20a01de0808364c26836cc8f47ed3b59a40a927d Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:40 +0200 Subject: [PATCH 694/868] regulator: dt-bindings: qcom,rpmh: Add PMR735B compatible Add the PMR735B compatible for the regulators in the PMIC found with the Milos SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-2-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- .../bindings/regulator/qcom,rpmh-regulator.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3dd150e5dad89..4c5b0629aa3e6 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -54,6 +54,7 @@ description: | For PMI8998, bob For PMC8380, smps1 - smps8, ldo1 - lodo3 For PMR735A, smps1 - smps3, ldo1 - ldo7 + For PMR735B, ldo1 - ldo12 For PMX55, smps1 - smps7, ldo1 - ldo16 For PMX65, smps1 - smps8, ldo1 - ldo21 For PMX75, smps1 - smps10, ldo1 - ldo21 @@ -89,6 +90,7 @@ properties: - qcom,pmm8155au-rpmh-regulators - qcom,pmm8654au-rpmh-regulators - qcom,pmr735a-rpmh-regulators + - qcom,pmr735b-rpmh-regulators - qcom,pmx55-rpmh-regulators - qcom,pmx65-rpmh-regulators - qcom,pmx75-rpmh-regulators @@ -445,6 +447,18 @@ allOf: patternProperties: "^vdd-s[1-3]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pmr735b-rpmh-regulators + then: + properties: + vdd-l1-l2-supply: true + vdd-l7-l8-supply: true + patternProperties: + "^vdd-l([3-6]|9|1[0-2])-supply$": true + - if: properties: compatible: -- GitLab From 28758434900ff4c4dce4e104fb5982ef3c0141ba Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:41 +0200 Subject: [PATCH 695/868] regulator: qcom-rpmh: add support for pmr735b regulators Add RPMH regulators exposed by Qualcomm Technologies, Inc. PMR735B PMIC. It has 12 LDOs with 2 different types, L4 & L10 are LDO512 LV PMOS and the rest are LDO512 NMOS. Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-3-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 7870722b6ee21..7b1743d51fd14 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1476,6 +1476,22 @@ static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmr735b_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo_lv, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), + {} +}; + static const struct rpmh_vreg_init_data pm660_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), @@ -1667,6 +1683,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pmr735a-rpmh-regulators", .data = pmr735a_vreg_data, }, + { + .compatible = "qcom,pmr735b-rpmh-regulators", + .data = pmr735b_vreg_data, + }, { .compatible = "qcom,pm660-rpmh-regulators", .data = pm660_vreg_data, -- GitLab From 3aa47d2ec83316c24e1ed15a492b331802dc6a69 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:42 +0200 Subject: [PATCH 696/868] regulator: qcom-rpmh: add support for pm7550 regulators Add RPMH regulators exposed by Qualcomm Technologies, Inc. PM7550 PMIC. It has 6 FTS525 (FT-SMPS) and 23 LDOs with 3 different types. L1-L11 are LDO515 LV NMOS, L12-L13 are LDO515 MV PMOS, L14-L23 are LDO512 MV PMOS. Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-4-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 7b1743d51fd14..109f0aae09b1d 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1462,6 +1462,40 @@ static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm7550_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo515_mv, "vdd-l12-l14"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo515_mv, "vdd-l13-l16"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l12-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo22", "ldo%s22", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo23", "ldo%s23", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), @@ -1679,6 +1713,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm7325-rpmh-regulators", .data = pm7325_vreg_data, }, + { + .compatible = "qcom,pm7550-rpmh-regulators", + .data = pm7550_vreg_data, + }, { .compatible = "qcom,pmr735a-rpmh-regulators", .data = pmr735a_vreg_data, -- GitLab From 43728a6434f9eca0385fd180d8452a5071678a5b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 14 Jul 2025 09:04:56 +0800 Subject: [PATCH 697/868] regulator: tps6286x-regulator: Fix a copy & paste error The volatile_reg function is named as tps6287x_volatile_reg by mistake when enabing the REGCACHE_MAPLE support. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250714010456.4906-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6286x-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c index 778f169b0acc6..e29aab06bf79e 100644 --- a/drivers/regulator/tps6286x-regulator.c +++ b/drivers/regulator/tps6286x-regulator.c @@ -25,7 +25,7 @@ #define TPS6286X_MAX_MV 1675 #define TPS6286X_STEP_MV 5 -static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +static bool tps6286x_volatile_reg(struct device *dev, unsigned int reg) { return reg == TPS6286X_STATUS; } @@ -34,7 +34,7 @@ static const struct regmap_config tps6286x_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_MAPLE, - .volatile_reg = tps6287x_volatile_reg, + .volatile_reg = tps6286x_volatile_reg, }; static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) -- GitLab From db12cdc8224845bbe31c1d7e5e7d2c2dde4847dc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:38 +0200 Subject: [PATCH 698/868] gpio: vx855: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-1-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-vx855.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 8fd6c3913d693..a3bceac7854c0 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -127,8 +127,7 @@ static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr) return ret; } -static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, - int val) +static int vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { struct vx855_gpio *vg = gpiochip_get_data(gpio); unsigned long flags; @@ -136,7 +135,7 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, /* True GPI cannot be switched to output mode */ if (nr < NR_VX855_GPI) - return; + return -EPERM; spin_lock_irqsave(&vg->lock, flags); reg_out = inl(vg->io_gpo); @@ -153,6 +152,8 @@ static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, } outl(reg_out, vg->io_gpo); spin_unlock_irqrestore(&vg->lock, flags); + + return 0; } static int vx855gpio_direction_output(struct gpio_chip *gpio, @@ -215,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg) c->direction_input = vx855gpio_direction_input; c->direction_output = vx855gpio_direction_output; c->get = vx855gpio_get; - c->set = vx855gpio_set; + c->set_rv = vx855gpio_set; c->set_config = vx855gpio_set_config; c->dbg_show = NULL; c->base = 0; -- GitLab From ff0f0d7c6587e38c308be9905e36f86e98fb9c1f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:39 +0200 Subject: [PATCH 699/868] gpio: wcd934x: check the return value of regmap_update_bits() regmap_update_bits() can fail so check its return value in wcd_gpio_direction_output() for consistency with the rest of the code and propagate any errors. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-2-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wcd934x.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c index 2bba27b13947f..cfa7b0a50c8e3 100644 --- a/drivers/gpio/gpio-wcd934x.c +++ b/drivers/gpio/gpio-wcd934x.c @@ -46,9 +46,12 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int val) { struct wcd_gpio_data *data = gpiochip_get_data(chip); + int ret; - regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, - WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + ret = regmap_update_bits(data->map, WCD_REG_DIR_CTL_OFFSET, + WCD_PIN_MASK(pin), WCD_PIN_MASK(pin)); + if (ret) + return ret; return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, WCD_PIN_MASK(pin), -- GitLab From 637c3054e9a5337d303577584d575c247138dae9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:40 +0200 Subject: [PATCH 700/868] gpio: wcd934x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-3-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wcd934x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-wcd934x.c b/drivers/gpio/gpio-wcd934x.c index cfa7b0a50c8e3..c89da9a220168 100644 --- a/drivers/gpio/gpio-wcd934x.c +++ b/drivers/gpio/gpio-wcd934x.c @@ -68,12 +68,13 @@ static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) return !!(value & WCD_PIN_MASK(pin)); } -static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) +static int wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val) { struct wcd_gpio_data *data = gpiochip_get_data(chip); - regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, - WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0); + return regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET, + WCD_PIN_MASK(pin), + val ? WCD_PIN_MASK(pin) : 0); } static int wcd_gpio_probe(struct platform_device *pdev) @@ -97,7 +98,7 @@ static int wcd_gpio_probe(struct platform_device *pdev) chip->direction_output = wcd_gpio_direction_output; chip->get_direction = wcd_gpio_get_direction; chip->get = wcd_gpio_get; - chip->set = wcd_gpio_set; + chip->set_rv = wcd_gpio_set; chip->parent = dev; chip->base = -1; chip->ngpio = WCD934X_NPINS; -- GitLab From dd94adf7da36281b5d1bc40ee8ac265082576f4c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:42 +0200 Subject: [PATCH 701/868] gpio: winbond: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-5-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-winbond.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-winbond.c b/drivers/gpio/gpio-winbond.c index 4b61d975cc0ec..421655b5d4c24 100644 --- a/drivers/gpio/gpio-winbond.c +++ b/drivers/gpio/gpio-winbond.c @@ -458,17 +458,19 @@ static int winbond_gpio_direction_out(struct gpio_chip *gc, return 0; } -static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, - int val) +static int winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) { unsigned long *base = gpiochip_get_data(gc); const struct winbond_gpio_info *info; + int ret; if (!winbond_gpio_get_info(&offset, &info)) - return; + return -EACCES; - if (winbond_sio_enter(*base) != 0) - return; + ret = winbond_sio_enter(*base); + if (ret) + return ret; winbond_sio_select_logical(*base, info->dev); @@ -481,6 +483,8 @@ static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, winbond_sio_reg_bclear(*base, info->datareg, offset); winbond_sio_leave(*base); + + return 0; } static struct gpio_chip winbond_gpio_chip = { @@ -490,7 +494,7 @@ static struct gpio_chip winbond_gpio_chip = { .can_sleep = true, .get = winbond_gpio_get, .direction_input = winbond_gpio_direction_in, - .set = winbond_gpio_set, + .set_rv = winbond_gpio_set, .direction_output = winbond_gpio_direction_out, }; -- GitLab From 023a24f83edf9abe0fbb66929d286cec5a09afbb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:43 +0200 Subject: [PATCH 702/868] gpio: wm831x: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-6-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wm831x.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 61bb83a1e8ae4..ab58aa7c0b996 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -58,13 +58,14 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) return 0; } -static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm831x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm831x_gpio *wm831x_gpio = gpiochip_get_data(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; - wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, - value << offset); + return wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, + value << offset); } static int wm831x_gpio_direction_out(struct gpio_chip *chip, @@ -85,9 +86,7 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip, return ret; /* Can only set GPIO state once it's in output mode */ - wm831x_gpio_set(chip, offset, value); - - return 0; + return wm831x_gpio_set(chip, offset, value); } static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -254,7 +253,7 @@ static const struct gpio_chip template_chip = { .direction_input = wm831x_gpio_direction_in, .get = wm831x_gpio_get, .direction_output = wm831x_gpio_direction_out, - .set = wm831x_gpio_set, + .set_rv = wm831x_gpio_set, .to_irq = wm831x_gpio_to_irq, .set_config = wm831x_set_config, .dbg_show = wm831x_gpio_dbg_show, -- GitLab From f7a680e9c2e512f903c385ed1a71732389788aa0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:44 +0200 Subject: [PATCH 703/868] gpio: wm8350: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-7-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wm8350.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index 2421cf606ed6f..9a7677f841fc6 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -48,15 +48,16 @@ static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset) return 0; } -static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8350_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8350_gpio_data *wm8350_gpio = gpiochip_get_data(chip); struct wm8350 *wm8350 = wm8350_gpio->wm8350; if (value) - wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); - else - wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + return wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); + + return wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset); } static int wm8350_gpio_direction_out(struct gpio_chip *chip, @@ -72,9 +73,7 @@ static int wm8350_gpio_direction_out(struct gpio_chip *chip, return ret; /* Don't have an atomic direction/value setup */ - wm8350_gpio_set(chip, offset, value); - - return 0; + return wm8350_gpio_set(chip, offset, value); } static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -94,7 +93,7 @@ static const struct gpio_chip template_chip = { .direction_input = wm8350_gpio_direction_in, .get = wm8350_gpio_get, .direction_output = wm8350_gpio_direction_out, - .set = wm8350_gpio_set, + .set_rv = wm8350_gpio_set, .to_irq = wm8350_gpio_to_irq, .can_sleep = true, }; -- GitLab From 47b427311d959fd3235485c08f367cd14df282ab Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:45 +0200 Subject: [PATCH 704/868] gpio: wm8994: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Charles Keepax Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-8-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wm8994.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index bf05c9b5882b8..ccc005628dd27 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -89,7 +89,8 @@ static int wm8994_gpio_direction_out(struct gpio_chip *chip, WM8994_GPN_DIR | WM8994_GPN_LVL, value); } -static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int wm8994_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct wm8994_gpio *wm8994_gpio = gpiochip_get_data(chip); struct wm8994 *wm8994 = wm8994_gpio->wm8994; @@ -97,7 +98,8 @@ static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) if (value) value = WM8994_GPN_LVL; - wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value); + return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, + value); } static int wm8994_gpio_set_config(struct gpio_chip *chip, unsigned int offset, @@ -254,7 +256,7 @@ static const struct gpio_chip template_chip = { .direction_input = wm8994_gpio_direction_in, .get = wm8994_gpio_get, .direction_output = wm8994_gpio_direction_out, - .set = wm8994_gpio_set, + .set_rv = wm8994_gpio_set, .set_config = wm8994_gpio_set_config, .to_irq = wm8994_gpio_to_irq, .dbg_show = wm8994_gpio_dbg_show, -- GitLab From 0933fc87f31da17871d6cf255dd9a3de86658685 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:46 +0200 Subject: [PATCH 705/868] gpio: xgene: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-9-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xgene.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index fb4b0c67aeef4..28f794e5eb26c 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -62,7 +62,7 @@ static void __xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) iowrite32(setval, chip->base + bank_offset); } -static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) +static int xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct xgene_gpio *chip = gpiochip_get_data(gc); unsigned long flags; @@ -70,6 +70,8 @@ static void xgene_gpio_set(struct gpio_chip *gc, unsigned int offset, int val) spin_lock_irqsave(&chip->lock, flags); __xgene_gpio_set(gc, offset, val); spin_unlock_irqrestore(&chip->lock, flags); + + return 0; } static int xgene_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) @@ -176,7 +178,7 @@ static int xgene_gpio_probe(struct platform_device *pdev) gpio->chip.direction_input = xgene_gpio_dir_in; gpio->chip.direction_output = xgene_gpio_dir_out; gpio->chip.get = xgene_gpio_get; - gpio->chip.set = xgene_gpio_set; + gpio->chip.set_rv = xgene_gpio_set; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; -- GitLab From 1919ea19a4ff8d534fb2789453a69f86f70a493b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:47 +0200 Subject: [PATCH 706/868] gpio: xilinx: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-10-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index c58a7e1349b4b..aaaa741179805 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -148,7 +148,7 @@ static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) * This function writes the specified value in to the specified signal of the * GPIO device. */ -static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static int xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { unsigned long flags; struct xgpio_instance *chip = gpiochip_get_data(gc); @@ -162,6 +162,8 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) xgpio_write_ch(chip, XGPIO_DATA_OFFSET, bit, chip->state); raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; } /** @@ -600,7 +602,7 @@ static int xgpio_probe(struct platform_device *pdev) chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_output = xgpio_dir_out; chip->gc.get = xgpio_get; - chip->gc.set = xgpio_set; + chip->gc.set_rv = xgpio_set; chip->gc.request = xgpio_request; chip->gc.free = xgpio_free; chip->gc.set_multiple = xgpio_set_multiple; -- GitLab From c719fd3e3991b16460babf70036bf39cdbbb7a90 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:48 +0200 Subject: [PATCH 707/868] gpio: xlp: drop unneeded ngpio checks GPIO core already makes sure that offsets higher than the number of GPIOs are never passed to controller callbacks. We can remove the unnecessary check. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-11-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xlp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index b4b52213bcd95..35b376c73ce31 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -206,7 +206,6 @@ static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1); return 0; @@ -216,7 +215,6 @@ static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0); return 0; @@ -226,7 +224,6 @@ static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); return xlp_gpio_get_reg(priv->gpio_paddrv, gpio); } @@ -234,7 +231,6 @@ static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); - BUG_ON(gpio >= gc->ngpio); xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); } -- GitLab From 6d0f71cd58aaf107dab3ea5a50e9f725d4691043 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:49 +0200 Subject: [PATCH 708/868] gpio: xlp: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-12-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xlp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 35b376c73ce31..bcd2dfec462d0 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -227,11 +227,13 @@ static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio) return xlp_gpio_get_reg(priv->gpio_paddrv, gpio); } -static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state) +static int xlp_gpio_set(struct gpio_chip *gc, unsigned int gpio, int state) { struct xlp_gpio_priv *priv = gpiochip_get_data(gc); xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state); + + return 0; } static int xlp_gpio_probe(struct platform_device *pdev) @@ -272,7 +274,7 @@ static int xlp_gpio_probe(struct platform_device *pdev) gc->ngpio = 70; gc->direction_output = xlp_gpio_dir_output; gc->direction_input = xlp_gpio_dir_input; - gc->set = xlp_gpio_set; + gc->set_rv = xlp_gpio_set; gc->get = xlp_gpio_get; spin_lock_init(&priv->lock); -- GitLab From ae8bcae8487293cb1de201734ab4779a2438618a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:50 +0200 Subject: [PATCH 709/868] gpio: xra1403: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-13-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xra1403.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 842cf875bb920..70402c6b54073 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -102,16 +102,13 @@ static int xra1403_get(struct gpio_chip *chip, unsigned int offset) return !!(val & BIT(offset % 8)); } -static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) +static int xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) { - int ret; struct xra1403 *xra = gpiochip_get_data(chip); - ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), - BIT(offset % 8), value ? BIT(offset % 8) : 0); - if (ret) - dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n", - offset, ret); + return regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), + value ? BIT(offset % 8) : 0); } #ifdef CONFIG_DEBUG_FS @@ -167,7 +164,7 @@ static int xra1403_probe(struct spi_device *spi) xra->chip.direction_output = xra1403_direction_output; xra->chip.get_direction = xra1403_get_direction; xra->chip.get = xra1403_get; - xra->chip.set = xra1403_set; + xra->chip.set_rv = xra1403_set; xra->chip.dbg_show = xra1403_dbg_show; -- GitLab From 735ddc67ab88d35378cb79cfa5ac5f87db0775fb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:51 +0200 Subject: [PATCH 710/868] gpio: xtensa: remove unneeded .set() callback GPIO core deals just fine with input-only controllers not implementing the .set() callback. Remove the unneeded dummy implementation. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-14-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xtensa.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index c8af34a6368f4..341b691ac2345 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -86,12 +86,6 @@ static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset) return !!(impwire & BIT(offset)); } -static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset, - int value) -{ - BUG(); /* output only; should never be called */ -} - static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset) { return GPIO_LINE_DIRECTION_OUT; /* output only */ @@ -128,7 +122,6 @@ static struct gpio_chip impwire_chip = { .ngpio = 32, .get_direction = xtensa_impwire_get_direction, .get = xtensa_impwire_get_value, - .set = xtensa_impwire_set_value, }; static struct gpio_chip expstate_chip = { -- GitLab From 383a02f6d421bf7703703aeb3e7270426b2fc30c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:52 +0200 Subject: [PATCH 711/868] gpio: xtensa: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-15-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xtensa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index 341b691ac2345..e7ff3c60324dc 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -103,7 +103,7 @@ static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset) return !!(expstate & BIT(offset)); } -static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset, +static int xtensa_expstate_set_value(struct gpio_chip *gc, unsigned int offset, int value) { unsigned long flags, saved_cpenable; @@ -114,6 +114,8 @@ static void xtensa_expstate_set_value(struct gpio_chip *gc, unsigned offset, __asm__ __volatile__("wrmsk_expstate %0, %1" :: "a" (val), "a" (mask)); disable_cp(flags, saved_cpenable); + + return 0; } static struct gpio_chip impwire_chip = { @@ -130,7 +132,7 @@ static struct gpio_chip expstate_chip = { .ngpio = 32, .get_direction = xtensa_expstate_get_direction, .get = xtensa_expstate_get_value, - .set = xtensa_expstate_set_value, + .set_rv = xtensa_expstate_set_value, }; static int xtensa_gpio_probe(struct platform_device *pdev) -- GitLab From ee6e05eb5fe27a45678848fb92a2a5db6b3fea84 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:53 +0200 Subject: [PATCH 712/868] gpio: zevio: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-16-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zevio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c index d7230fd83f5d6..0799f79767107 100644 --- a/drivers/gpio/gpio-zevio.c +++ b/drivers/gpio/gpio-zevio.c @@ -91,7 +91,7 @@ static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin) return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1; } -static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +static int zevio_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct zevio_gpio *controller = gpiochip_get_data(chip); u32 val; @@ -105,6 +105,8 @@ static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value) zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val); spin_unlock(&controller->lock); + + return 0; } static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin) @@ -159,7 +161,7 @@ static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin) static const struct gpio_chip zevio_gpio_chip = { .direction_input = zevio_gpio_direction_input, .direction_output = zevio_gpio_direction_output, - .set = zevio_gpio_set, + .set_rv = zevio_gpio_set, .get = zevio_gpio_get, .to_irq = zevio_gpio_to_irq, .base = 0, -- GitLab From 815c9769ba0e2e184eac570e596391a1ca58b8c8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:54 +0200 Subject: [PATCH 713/868] gpio: zynq: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-17-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynq.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 3dae63f3ea217..b22b4e25c68dc 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -265,8 +265,8 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin) * upper 16 bits) based on the given pin number and sets the state of a * gpio pin to the specified value. The state is either 0 or non-zero. */ -static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, - int state) +static int zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) { unsigned int reg_offset, bank_num, bank_pin_num; struct zynq_gpio *gpio = gpiochip_get_data(chip); @@ -290,6 +290,8 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, ((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK); writel_relaxed(state, gpio->base_addr + reg_offset); + + return 0; } /** @@ -930,7 +932,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) chip->owner = THIS_MODULE; chip->parent = &pdev->dev; chip->get = zynq_gpio_get_value; - chip->set = zynq_gpio_set_value; + chip->set_rv = zynq_gpio_set_value; chip->request = zynq_gpio_request; chip->free = zynq_gpio_free; chip->direction_input = zynq_gpio_dir_in; -- GitLab From 680450b358b73823c82d150330aacc52e286be08 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:55 +0200 Subject: [PATCH 714/868] gpio: zynqmp-modepin: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-18-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynqmp-modepin.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c index 2f3c9ebfa78d1..36a547d6fc5a5 100644 --- a/drivers/gpio/gpio-zynqmp-modepin.c +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -57,8 +57,8 @@ static int modepin_gpio_get_value(struct gpio_chip *chip, unsigned int pin) * * Return: None. */ -static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, - int state) +static int modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) { u32 bootpin_val = 0; int ret; @@ -77,6 +77,8 @@ static void modepin_gpio_set_value(struct gpio_chip *chip, unsigned int pin, ret = zynqmp_pm_bootmode_write(bootpin_val); if (ret) pr_err("modepin: set value error %d for pin %d\n", ret, pin); + + return ret; } /** @@ -128,7 +130,7 @@ static int modepin_gpio_probe(struct platform_device *pdev) chip->owner = THIS_MODULE; chip->parent = &pdev->dev; chip->get = modepin_gpio_get_value; - chip->set = modepin_gpio_set_value; + chip->set_rv = modepin_gpio_set_value; chip->direction_input = modepin_gpio_dir_in; chip->direction_output = modepin_gpio_dir_out; chip->label = dev_name(&pdev->dev); -- GitLab From e70513bd98e3912b431b196e3cf53ac821598e6a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 9 Jul 2025 08:41:56 +0200 Subject: [PATCH 715/868] gpio: zynqmp-modepin: set line value in .direction_output() It's ok to not do anything specific when setting direction but the callback should still respect the line value the user requests. Link: https://lore.kernel.org/r/20250709-gpiochip-set-rv-gpio-remaining-v1-19-b8950f69618d@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-zynqmp-modepin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-zynqmp-modepin.c b/drivers/gpio/gpio-zynqmp-modepin.c index 36a547d6fc5a5..6dc5d7acb89c5 100644 --- a/drivers/gpio/gpio-zynqmp-modepin.c +++ b/drivers/gpio/gpio-zynqmp-modepin.c @@ -104,7 +104,7 @@ static int modepin_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) static int modepin_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, int state) { - return 0; + return modepin_gpio_set_value(chip, pin, state); } /** -- GitLab From 6f8584a4826f01a55d3d0c4bbad5961f1de52fc9 Mon Sep 17 00:00:00 2001 From: Raphael Gallais-Pou Date: Mon, 9 Jun 2025 23:21:09 +0200 Subject: [PATCH 716/868] spi: st: Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr() Letting the compiler remove these functions when the kernel is built without CONFIG_PM_SLEEP support is simpler and less error prone than the use of #ifdef based kernel configuration guards. Signed-off-by: Raphael Gallais-Pou Link: https://patch.msgid.link/20250609-update_pm_macro-v1-1-819a53ef0eed@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 4cff976ab16fb..49ab4c515156f 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -378,8 +378,7 @@ static void spi_st_remove(struct platform_device *pdev) pinctrl_pm_select_sleep_state(&pdev->dev); } -#ifdef CONFIG_PM -static int spi_st_runtime_suspend(struct device *dev) +static int __maybe_unused spi_st_runtime_suspend(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); struct spi_st *spi_st = spi_controller_get_devdata(host); @@ -392,7 +391,7 @@ static int spi_st_runtime_suspend(struct device *dev) return 0; } -static int spi_st_runtime_resume(struct device *dev) +static int __maybe_unused spi_st_runtime_resume(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); struct spi_st *spi_st = spi_controller_get_devdata(host); @@ -403,10 +402,8 @@ static int spi_st_runtime_resume(struct device *dev) return ret; } -#endif -#ifdef CONFIG_PM_SLEEP -static int spi_st_suspend(struct device *dev) +static int __maybe_unused spi_st_suspend(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); int ret; @@ -418,7 +415,7 @@ static int spi_st_suspend(struct device *dev) return pm_runtime_force_suspend(dev); } -static int spi_st_resume(struct device *dev) +static int __maybe_unused spi_st_resume(struct device *dev) { struct spi_controller *host = dev_get_drvdata(dev); int ret; @@ -429,7 +426,6 @@ static int spi_st_resume(struct device *dev) return pm_runtime_force_resume(dev); } -#endif static const struct dev_pm_ops spi_st_pm = { SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) @@ -445,7 +441,7 @@ MODULE_DEVICE_TABLE(of, stm_spi_match); static struct platform_driver spi_st_driver = { .driver = { .name = "spi-st", - .pm = &spi_st_pm, + .pm = pm_sleep_ptr(&spi_st_pm), .of_match_table = of_match_ptr(stm_spi_match), }, .probe = spi_st_probe, -- GitLab From 8a4d73121d6bd9a70895e65d6d1014ed6b0a6c8e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:07 -0400 Subject: [PATCH 717/868] ASoC: codecs: da7219: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-1-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 64 +++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 3958e88a24456..05e4cc89df220 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1982,8 +1982,8 @@ static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw, } } -static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int da7219_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, @@ -1992,28 +1992,30 @@ static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate, if (!da7219->master) return -EINVAL; - if (rate < 11025) - return 8000; - else if (rate < 12000) - return 11025; - else if (rate < 16000) - return 12000; - else if (rate < 22050) - return 16000; - else if (rate < 24000) - return 22050; - else if (rate < 32000) - return 24000; - else if (rate < 44100) - return 32000; - else if (rate < 48000) - return 44100; - else if (rate < 88200) - return 48000; - else if (rate < 96000) - return 88200; + if (req->rate < 11025) + req->rate = 8000; + else if (req->rate < 12000) + req->rate = 11025; + else if (req->rate < 16000) + req->rate = 12000; + else if (req->rate < 22050) + req->rate = 16000; + else if (req->rate < 24000) + req->rate = 22050; + else if (req->rate < 32000) + req->rate = 24000; + else if (req->rate < 44100) + req->rate = 32000; + else if (req->rate < 48000) + req->rate = 44100; + else if (req->rate < 88200) + req->rate = 48000; + else if (req->rate < 96000) + req->rate = 88200; else - return 96000; + req->rate = 96000; + + return 0; } static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2070,15 +2072,15 @@ static unsigned long da7219_bclk_get_factor(unsigned long rate, return 256; } -static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int da7219_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw[DA7219_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !da7219->master) + if (!req->best_parent_rate || !da7219->master) return -EINVAL; /* @@ -2088,9 +2090,11 @@ static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * parent WCLK rate set and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = da7219_bclk_get_factor(rate, *parent_rate); + factor = da7219_bclk_get_factor(req->rate, req->best_parent_rate); + + req->rate = req->best_parent_rate * factor; - return *parent_rate * factor; + return 0; } static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2116,12 +2120,12 @@ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = { .unprepare = da7219_wclk_unprepare, .is_prepared = da7219_wclk_is_prepared, .recalc_rate = da7219_wclk_recalc_rate, - .round_rate = da7219_wclk_round_rate, + .determine_rate = da7219_wclk_determine_rate, .set_rate = da7219_wclk_set_rate, }, [DA7219_DAI_BCLK_IDX] = { .recalc_rate = da7219_bclk_recalc_rate, - .round_rate = da7219_bclk_round_rate, + .determine_rate = da7219_bclk_determine_rate, .set_rate = da7219_bclk_set_rate, }, }; -- GitLab From 4e15a10f6fb254e33d73a6da3c4d00e3e64d2eb8 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:08 -0400 Subject: [PATCH 718/868] ASoC: codecs: rt5682: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-2-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 7c88370e2dee6..a0abd2ce0c1e1 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2675,8 +2675,8 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, return rt5682->lrck[RT5682_AIF1]; } -static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, @@ -2689,13 +2689,13 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, * Only accept to set wclk rate to 44.1k or 48kHz. * It will force to 48kHz if not both. */ - if (rate != CLK_48 && rate != CLK_44) { + if (req->rate != CLK_48 && req->rate != CLK_44) { dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); - rate = CLK_48; + req->rate = CLK_48; } - return rate; + return 0; } static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2795,15 +2795,15 @@ static unsigned long rt5682_bclk_get_factor(unsigned long rate, return 256; } -static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !rt5682_clk_check(rt5682)) + if (!req->best_parent_rate || !rt5682_clk_check(rt5682)) return -EINVAL; /* @@ -2813,9 +2813,11 @@ static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = rt5682_bclk_get_factor(rate, *parent_rate); + factor = rt5682_bclk_get_factor(req->rate, req->best_parent_rate); + + req->rate = req->best_parent_rate * factor; - return *parent_rate * factor; + return 0; } static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2849,12 +2851,12 @@ static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { .prepare = rt5682_wclk_prepare, .unprepare = rt5682_wclk_unprepare, .recalc_rate = rt5682_wclk_recalc_rate, - .round_rate = rt5682_wclk_round_rate, + .determine_rate = rt5682_wclk_determine_rate, .set_rate = rt5682_wclk_set_rate, }, [RT5682_DAI_BCLK_IDX] = { .recalc_rate = rt5682_bclk_recalc_rate, - .round_rate = rt5682_bclk_round_rate, + .determine_rate = rt5682_bclk_determine_rate, .set_rate = rt5682_bclk_set_rate, }, }; -- GitLab From a37d9c8aef1c78876eff0bc8980a889c083de89d Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:09 -0400 Subject: [PATCH 719/868] ASoC: codecs: rt5682s: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-3-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682s.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 73c4b3c31f8c4..80b921695e7d1 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -2610,8 +2610,8 @@ static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, return rt5682s->lrck[RT5682S_AIF1]; } -static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682s_wclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682s_priv *rt5682s = container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); @@ -2624,13 +2624,13 @@ static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate, * Only accept to set wclk rate to 44.1k or 48kHz. * It will force to 48kHz if not both. */ - if (rate != CLK_48 && rate != CLK_44) { + if (req->rate != CLK_48 && req->rate != CLK_44) { dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); - rate = CLK_48; + req->rate = CLK_48; } - return rate; + return 0; } static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2719,14 +2719,14 @@ static unsigned long rt5682s_bclk_get_factor(unsigned long rate, return 256; } -static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int rt5682s_bclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct rt5682s_priv *rt5682s = container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]); unsigned long factor; - if (!*parent_rate || !rt5682s_clk_check(rt5682s)) + if (!req->best_parent_rate || !rt5682s_clk_check(rt5682s)) return -EINVAL; /* @@ -2736,9 +2736,11 @@ static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate, * and find the appropriate multiplier of BCLK to * get the rounded down BCLK value. */ - factor = rt5682s_bclk_get_factor(rate, *parent_rate); + factor = rt5682s_bclk_get_factor(req->rate, req->best_parent_rate); + + req->rate = req->best_parent_rate * factor; - return *parent_rate * factor; + return 0; } static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -2769,12 +2771,12 @@ static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = { .prepare = rt5682s_wclk_prepare, .unprepare = rt5682s_wclk_unprepare, .recalc_rate = rt5682s_wclk_recalc_rate, - .round_rate = rt5682s_wclk_round_rate, + .determine_rate = rt5682s_wclk_determine_rate, .set_rate = rt5682s_wclk_set_rate, }, [RT5682S_DAI_BCLK_IDX] = { .recalc_rate = rt5682s_bclk_recalc_rate, - .round_rate = rt5682s_bclk_round_rate, + .determine_rate = rt5682s_bclk_determine_rate, .set_rate = rt5682s_bclk_set_rate, }, }; -- GitLab From fc62ed665eb2e8fb0f1e12ab9cdb578666704a76 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:10 -0400 Subject: [PATCH 720/868] ASoC: qcom: qdsp6: q6dsp-lpass-clocks: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-4-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c index e758411603be5..03838582aeade 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c @@ -69,17 +69,17 @@ static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw, return clk->rate; } -static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_q6dsp_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - return rate; + return 0; } static const struct clk_ops clk_q6dsp_ops = { .prepare = clk_q6dsp_prepare, .unprepare = clk_q6dsp_unprepare, .set_rate = clk_q6dsp_set_rate, - .round_rate = clk_q6dsp_round_rate, + .determine_rate = clk_q6dsp_determine_rate, .recalc_rate = clk_q6dsp_recalc_rate, }; -- GitLab From afd529d740028a41fa750d4491b106cecbccba3e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:11 -0400 Subject: [PATCH 721/868] ASoC: stm: stm32_i2s: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-5-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_i2s.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 6037b7a9c97bf..0e489097d9c10 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -461,20 +461,25 @@ err: return -EINVAL; } -static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int stm32_i2smclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_i2smclk_data *mclk = to_mclk_data(hw); struct stm32_i2s_data *i2s = mclk->i2s_data; int ret; - ret = stm32_i2s_calc_clk_div(i2s, *prate, rate); - if (ret) - return ret; + ret = stm32_i2s_calc_clk_div(i2s, req->best_parent_rate, req->rate); + if (ret) { + req->rate = ret; - mclk->freq = *prate / i2s->divider; + return 0; + } - return mclk->freq; + mclk->freq = req->best_parent_rate / i2s->divider; + + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_i2smclk_recalc_rate(struct clk_hw *hw, @@ -530,7 +535,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_i2smclk_enable, .disable = stm32_i2smclk_disable, .recalc_rate = stm32_i2smclk_recalc_rate, - .round_rate = stm32_i2smclk_round_rate, + .determine_rate = stm32_i2smclk_determine_rate, .set_rate = stm32_i2smclk_set_rate, }; -- GitLab From d5f317fd5cd9dfdf5bbe11384001817760c12b75 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 11:51:12 -0400 Subject: [PATCH 722/868] ASoC: stm: stm32_sai_sub: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Link: https://patch.msgid.link/20250710-sound-clk-round-rate-v1-6-4a9c3bb6ff3a@redhat.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index bf5299ba11c3c..463a2b7d023b9 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -489,20 +489,22 @@ err: return -EINVAL; } -static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int stm32_sai_mclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); struct stm32_sai_sub_data *sai = mclk->sai_data; int div; - div = stm32_sai_get_clk_div(sai, *prate, rate); + div = stm32_sai_get_clk_div(sai, req->best_parent_rate, req->rate); if (div <= 0) return -EINVAL; - mclk->freq = *prate / div; + mclk->freq = req->best_parent_rate / div; - return mclk->freq; + req->rate = mclk->freq; + + return 0; } static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, @@ -558,7 +560,7 @@ static const struct clk_ops mclk_ops = { .enable = stm32_sai_mclk_enable, .disable = stm32_sai_mclk_disable, .recalc_rate = stm32_sai_mclk_recalc_rate, - .round_rate = stm32_sai_mclk_round_rate, + .determine_rate = stm32_sai_mclk_determine_rate, .set_rate = stm32_sai_mclk_set_rate, }; -- GitLab From eb514766e0e0d2064c8700a79441e43e85008381 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 25 May 2025 14:32:30 +0200 Subject: [PATCH 723/868] thermal/drivers/loongson2: Constify struct thermal_zone_device_ops 'struct thermal_zone_device_ops' could be left unmodified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. This partly reverts commit 734b5def91b5 ("thermal/drivers/loongson2: Add Loongson-2K2000 support") which removed the const qualifier. Instead, define two different structures. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 5089 1160 0 6249 1869 drivers/thermal/loongson2_thermal.o After: ===== text data bss dec hex filename 5464 1128 0 6592 19c0 drivers/thermal/loongson2_thermal.o Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/5f5f815f85a9450bca7848c6d47a1fee840f47e5.1748176328.git.christophe.jaillet@wanadoo.fr Signed-off-by: Daniel Lezcano --- drivers/thermal/loongson2_thermal.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c index 2d6b75b0539f1..ea4dd2fb1f473 100644 --- a/drivers/thermal/loongson2_thermal.c +++ b/drivers/thermal/loongson2_thermal.c @@ -112,13 +112,19 @@ static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, return loongson2_thermal_set(data, low/MILLI, high/MILLI, true); } -static struct thermal_zone_device_ops loongson2_of_thermal_ops = { +static const struct thermal_zone_device_ops loongson2_2k1000_of_thermal_ops = { .get_temp = loongson2_2k1000_get_temp, .set_trips = loongson2_thermal_set_trips, }; +static const struct thermal_zone_device_ops loongson2_2k2000_of_thermal_ops = { + .get_temp = loongson2_2k2000_get_temp, + .set_trips = loongson2_thermal_set_trips, +}; + static int loongson2_thermal_probe(struct platform_device *pdev) { + const struct thermal_zone_device_ops *thermal_ops; struct device *dev = &pdev->dev; struct loongson2_thermal_data *data; struct thermal_zone_device *tzd; @@ -140,7 +146,9 @@ static int loongson2_thermal_probe(struct platform_device *pdev) if (IS_ERR(data->temp_reg)) return PTR_ERR(data->temp_reg); - loongson2_of_thermal_ops.get_temp = loongson2_2k2000_get_temp; + thermal_ops = &loongson2_2k2000_of_thermal_ops; + } else { + thermal_ops = &loongson2_2k1000_of_thermal_ops; } irq = platform_get_irq(pdev, 0); @@ -152,8 +160,7 @@ static int loongson2_thermal_probe(struct platform_device *pdev) loongson2_thermal_set(data, 0, 0, false); for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) { - tzd = devm_thermal_of_zone_register(dev, i, data, - &loongson2_of_thermal_ops); + tzd = devm_thermal_of_zone_register(dev, i, data, thermal_ops); if (!IS_ERR(tzd)) break; -- GitLab From 992e2ed0abf25d9567c61af9f3c552d6d9024e04 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 25 May 2025 11:40:04 +0200 Subject: [PATCH 724/868] thermal: Constify struct thermal_zone_device_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'struct thermal_zone_device_ops' are not modified in these drivers. Constifying these structures moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 28116 5168 128 33412 8284 drivers/thermal/armada_thermal.o After: ===== text data bss dec hex filename 28244 5040 128 33412 8284 drivers/thermal/armada_thermal.o Signed-off-by: Christophe JAILLET Reviewed-by: Frank Li Reviewed-by: Miquel Raynal # For Armada Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/5bba3bf0139e2418b306a0f9a2f1f81ef49e88a6.1748165978.git.christophe.jaillet@wanadoo.fr Signed-off-by: Daniel Lezcano --- drivers/thermal/armada_thermal.c | 2 +- drivers/thermal/da9062-thermal.c | 2 +- drivers/thermal/dove_thermal.c | 2 +- drivers/thermal/imx_thermal.c | 2 +- drivers/thermal/intel/int340x_thermal/int3400_thermal.c | 2 +- drivers/thermal/kirkwood_thermal.c | 2 +- drivers/thermal/mediatek/lvts_thermal.c | 2 +- drivers/thermal/renesas/rcar_thermal.c | 2 +- drivers/thermal/spear_thermal.c | 2 +- drivers/thermal/st/st_thermal.c | 2 +- drivers/thermal/testing/zone.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 9bff21068721c..c2fbdb534f61e 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -408,7 +408,7 @@ static int armada_get_temp_legacy(struct thermal_zone_device *thermal, return ret; } -static struct thermal_zone_device_ops legacy_ops = { +static const struct thermal_zone_device_ops legacy_ops = { .get_temp = armada_get_temp_legacy, }; diff --git a/drivers/thermal/da9062-thermal.c b/drivers/thermal/da9062-thermal.c index 2077e85ef5cae..a8d4b766ba21b 100644 --- a/drivers/thermal/da9062-thermal.c +++ b/drivers/thermal/da9062-thermal.c @@ -137,7 +137,7 @@ static int da9062_thermal_get_temp(struct thermal_zone_device *z, return 0; } -static struct thermal_zone_device_ops da9062_thermal_ops = { +static const struct thermal_zone_device_ops da9062_thermal_ops = { .get_temp = da9062_thermal_get_temp, }; diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index f9157a47156b0..723bc72f06264 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -106,7 +106,7 @@ static int dove_get_temp(struct thermal_zone_device *thermal, return 0; } -static struct thermal_zone_device_ops ops = { +static const struct thermal_zone_device_ops ops = { .get_temp = dove_get_temp, }; diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index bab52e6b3b155..38c993d1bcb32 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -361,7 +361,7 @@ static bool imx_should_bind(struct thermal_zone_device *tz, return trip->type == THERMAL_TRIP_PASSIVE; } -static struct thermal_zone_device_ops imx_tz_ops = { +static const struct thermal_zone_device_ops imx_tz_ops = { .should_bind = imx_should_bind, .get_temp = imx_get_temp, .change_mode = imx_change_mode, diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 0e07693ecf59e..5736638c586b2 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -515,7 +515,7 @@ eval_odvp: return result; } -static struct thermal_zone_device_ops int3400_thermal_ops = { +static const struct thermal_zone_device_ops int3400_thermal_ops = { .get_temp = int3400_thermal_get_temp, .change_mode = int3400_thermal_change_mode, }; diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index 7c22652316688..4619e090f7561 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -48,7 +48,7 @@ static int kirkwood_get_temp(struct thermal_zone_device *thermal, return 0; } -static struct thermal_zone_device_ops ops = { +static const struct thermal_zone_device_ops ops = { .get_temp = kirkwood_get_temp, }; diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 985925147ac06..acce8fde2cbad 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -571,7 +571,7 @@ static irqreturn_t lvts_irq_handler(int irq, void *data) return iret; } -static struct thermal_zone_device_ops lvts_ops = { +static const struct thermal_zone_device_ops lvts_ops = { .get_temp = lvts_get_temp, .set_trips = lvts_set_trips, }; diff --git a/drivers/thermal/renesas/rcar_thermal.c b/drivers/thermal/renesas/rcar_thermal.c index 00a66ee0a5b00..fdd7afdc4ff69 100644 --- a/drivers/thermal/renesas/rcar_thermal.c +++ b/drivers/thermal/renesas/rcar_thermal.c @@ -277,7 +277,7 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) return rcar_thermal_get_current_temp(priv, temp); } -static struct thermal_zone_device_ops rcar_thermal_zone_ops = { +static const struct thermal_zone_device_ops rcar_thermal_zone_ops = { .get_temp = rcar_thermal_get_temp, }; diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index bb96be9475213..603dadcd3df58 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -41,7 +41,7 @@ static inline int thermal_get_temp(struct thermal_zone_device *thermal, return 0; } -static struct thermal_zone_device_ops ops = { +static const struct thermal_zone_device_ops ops = { .get_temp = thermal_get_temp, }; diff --git a/drivers/thermal/st/st_thermal.c b/drivers/thermal/st/st_thermal.c index a14a37d546982..1470ca519def4 100644 --- a/drivers/thermal/st/st_thermal.c +++ b/drivers/thermal/st/st_thermal.c @@ -132,7 +132,7 @@ static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature) return 0; } -static struct thermal_zone_device_ops st_tz_ops = { +static const struct thermal_zone_device_ops st_tz_ops = { .get_temp = st_thermal_get_temp, }; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index 1f4e450100e2f..4257d813d572d 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -381,7 +381,7 @@ static int tt_zone_get_temp(struct thermal_zone_device *tz, int *temp) return 0; } -static struct thermal_zone_device_ops tt_zone_ops = { +static const struct thermal_zone_device_ops tt_zone_ops = { .get_temp = tt_zone_get_temp, }; -- GitLab From 7ee2c3c0dac3727b4e424c8e93de7d5d9d00bcac Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 11 Jun 2025 12:43:48 +0200 Subject: [PATCH 725/868] thermal: Use dev_fwnode() irq_domain_create_simple() takes fwnode as the first argument. It can be extracted from the struct device using dev_fwnode() helper instead of using of_node with of_fwnode_handle(). So use the dev_fwnode() helper. Signed-off-by: Jiri Slaby (SUSE) Cc: Amit Kucheria Cc: Thara Gopinath Cc: Rafael J. Wysocki Cc: Daniel Lezcano Cc: Zhang Rui Cc: Lukasz Luba Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-arm-msm@vger.kernel.org Cc: linux-tegra@vger.kernel.org Link: https://lore.kernel.org/r/20250611104348.192092-20-jirislaby@kernel.org Signed-off-by: Daniel Lezcano --- drivers/thermal/qcom/lmh.c | 3 +-- drivers/thermal/tegra/soctherm.c | 13 +++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/qcom/lmh.c b/drivers/thermal/qcom/lmh.c index 991d1573983d9..75eaa9a68ab8a 100644 --- a/drivers/thermal/qcom/lmh.c +++ b/drivers/thermal/qcom/lmh.c @@ -209,8 +209,7 @@ static int lmh_probe(struct platform_device *pdev) } lmh_data->irq = platform_get_irq(pdev, 0); - lmh_data->domain = irq_domain_create_linear(of_fwnode_handle(np), 1, &lmh_irq_ops, - lmh_data); + lmh_data->domain = irq_domain_create_linear(dev_fwnode(dev), 1, &lmh_irq_ops, lmh_data); if (!lmh_data->domain) { dev_err(dev, "Error adding irq_domain\n"); return -EINVAL; diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index 926f1052e6de0..53a5c649f4b14 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -1206,7 +1206,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = { /** * soctherm_oc_int_init() - Initial enabling of the over * current interrupts - * @np: The devicetree node for soctherm + * @fwnode: The devicetree node for soctherm * @num_irqs: The number of new interrupt requests * * Sets the over current interrupt request chip data @@ -1215,7 +1215,7 @@ static const struct irq_domain_ops soctherm_oc_domain_ops = { * -ENOMEM (out of memory), or irq_base if the function failed to * allocate the irqs */ -static int soctherm_oc_int_init(struct device_node *np, int num_irqs) +static int soctherm_oc_int_init(struct fwnode_handle *fwnode, int num_irqs) { if (!num_irqs) { pr_info("%s(): OC interrupts are not enabled\n", __func__); @@ -1234,10 +1234,8 @@ static int soctherm_oc_int_init(struct device_node *np, int num_irqs) soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; soc_irq_cdata.irq_chip.irq_set_wake = NULL; - soc_irq_cdata.domain = irq_domain_create_linear(of_fwnode_handle(np), num_irqs, - &soctherm_oc_domain_ops, - &soc_irq_cdata); - + soc_irq_cdata.domain = irq_domain_create_linear(fwnode, num_irqs, &soctherm_oc_domain_ops, + &soc_irq_cdata); if (!soc_irq_cdata.domain) { pr_err("%s: Failed to create IRQ domain\n", __func__); return -ENOMEM; @@ -1968,10 +1966,9 @@ static void tegra_soctherm_throttle(struct device *dev) static int soctherm_interrupts_init(struct platform_device *pdev, struct tegra_soctherm *tegra) { - struct device_node *np = pdev->dev.of_node; int ret; - ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); + ret = soctherm_oc_int_init(dev_fwnode(&pdev->dev), TEGRA_SOC_OC_IRQ_MAX); if (ret < 0) { dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); return ret; -- GitLab From 9b614ceada7cb846de1a1c3bb0b29b0a2726ef45 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 14 Jul 2025 15:52:04 -0300 Subject: [PATCH 726/868] rust: regulator: add a bare minimum regulator abstraction Add a bare minimum regulator abstraction to be used by Rust drivers. This abstraction adds a small subset of the regulator API, which is thought to be sufficient for the drivers we have now. Regulators provide the power needed by many hardware blocks and thus are likely to be needed by a lot of drivers. It was tested on rk3588, where it was used to power up the "mali" regulator in order to power up the GPU. Reviewed-by: Alexandre Courbot Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20250714-topics-tyr-regulator2-v8-1-c7ab3955d524@collabora.com Signed-off-by: Mark Brown --- rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/regulator.c | 43 ++++ rust/kernel/lib.rs | 1 + rust/kernel/regulator.rs | 418 ++++++++++++++++++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 rust/helpers/regulator.c create mode 100644 rust/kernel/regulator.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 8cbb660e2ec21..2d51f9d056091 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index b15b3cddad4eb..f1f26ba91d97a 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -33,6 +33,7 @@ #include "pci.c" #include "pid_namespace.c" #include "rbtree.c" +#include "regulator.c" #include "rcu.c" #include "refcount.c" #include "security.c" diff --git a/rust/helpers/regulator.c b/rust/helpers/regulator.c new file mode 100644 index 0000000000000..cd8b7ba648ee3 --- /dev/null +++ b/rust/helpers/regulator.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#ifndef CONFIG_REGULATOR + +void rust_helper_regulator_put(struct regulator *regulator) +{ + regulator_put(regulator); +} + +int rust_helper_regulator_set_voltage(struct regulator *regulator, int min_uV, + int max_uV) +{ + return regulator_set_voltage(regulator, min_uV, max_uV); +} + +int rust_helper_regulator_get_voltage(struct regulator *regulator) +{ + return regulator_get_voltage(regulator); +} + +struct regulator *rust_helper_regulator_get(struct device *dev, const char *id) +{ + return regulator_get(dev, id); +} + +int rust_helper_regulator_enable(struct regulator *regulator) +{ + return regulator_enable(regulator); +} + +int rust_helper_regulator_disable(struct regulator *regulator) +{ + return regulator_disable(regulator); +} + +int rust_helper_regulator_is_enabled(struct regulator *regulator) +{ + return regulator_is_enabled(regulator); +} + +#endif diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c37..5e4cd8c5e6ff1 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -100,6 +100,7 @@ pub mod platform; pub mod prelude; pub mod print; pub mod rbtree; +pub mod regulator; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs new file mode 100644 index 0000000000000..65f3a125348f2 --- /dev/null +++ b/rust/kernel/regulator.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Regulator abstractions, providing a standard kernel interface to control +//! voltage and current regulators. +//! +//! The intention is to allow systems to dynamically control regulator power +//! output in order to save power and prolong battery life. This applies to both +//! voltage regulators (where voltage output is controllable) and current sinks +//! (where current limit is controllable). +//! +//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h) +//! +//! Regulators are modeled in Rust with a collection of states. Each state may +//! enforce a given invariant, and they may convert between each other where applicable. +//! +//! See [Voltage and current regulator API](https://docs.kernel.org/driver-api/regulator.html) +//! for more information. + +use crate::{ + bindings, + device::Device, + error::{from_err_ptr, to_result, Result}, + prelude::*, +}; + +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +mod private { + pub trait Sealed {} + + impl Sealed for super::Enabled {} + impl Sealed for super::Disabled {} + impl Sealed for super::Dynamic {} +} + +/// A trait representing the different states a [`Regulator`] can be in. +pub trait RegulatorState: private::Sealed + 'static { + /// Whether the regulator should be disabled when dropped. + const DISABLE_ON_DROP: bool; +} + +/// A state where the [`Regulator`] is known to be enabled. +/// +/// The `enable` reference count held by this state is decremented when it is +/// dropped. +pub struct Enabled; + +/// A state where this [`Regulator`] handle has not specifically asked for the +/// underlying regulator to be enabled. This means that this reference does not +/// own an `enable` reference count, but the regulator may still be on. +pub struct Disabled; + +/// A state that models the C API. The [`Regulator`] can be either enabled or +/// disabled, and the user is in control of the reference count. This is also +/// the default state. +/// +/// Use [`Regulator::is_enabled`] to check the regulator's current state. +pub struct Dynamic; + +impl RegulatorState for Enabled { + const DISABLE_ON_DROP: bool = true; +} + +impl RegulatorState for Disabled { + const DISABLE_ON_DROP: bool = false; +} + +impl RegulatorState for Dynamic { + const DISABLE_ON_DROP: bool = false; +} + +/// A trait that abstracts the ability to check if a [`Regulator`] is enabled. +pub trait IsEnabled: RegulatorState {} +impl IsEnabled for Disabled {} +impl IsEnabled for Dynamic {} + +/// An error that can occur when trying to convert a [`Regulator`] between states. +pub struct Error { + /// The error that occurred. + pub error: kernel::error::Error, + + /// The regulator that caused the error, so that the operation may be retried. + pub regulator: Regulator, +} + +/// A `struct regulator` abstraction. +/// +/// # Examples +/// +/// ## Enabling a regulator +/// +/// This example uses [`Regulator`], which is suitable for drivers that +/// enable a regulator at probe time and leave them on until the device is +/// removed or otherwise shutdown. +/// +/// These users can store [`Regulator`] directly in their driver's +/// private data struct. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled}; +/// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // The voltage can be set before enabling the regulator if needed, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Enables the regulator, consuming the previous value. +/// // +/// // From now on, the regulator is known to be enabled because of the type +/// // `Enabled`. +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_enabled().map_err(|error| error.error)?; +/// +/// // The voltage can also be set after enabling the regulator, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// A more concise shortcut is available for enabling a regulator. This is +/// equivalent to `regulator_get_enable()`: +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Enabled}; +/// fn enable(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator and enable it. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Disabling a regulator +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Enabled, Disabled}; +/// fn disable(dev: &Device, regulator: Regulator) -> Result { +/// // We can also disable an enabled regulator without reliquinshing our +/// // refcount: +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_disabled().map_err(|error| error.error)?; +/// +/// // The refcount will be decremented when `regulator` is dropped. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Using [`Regulator`] +/// +/// This example mimics the behavior of the C API, where the user is in +/// control of the enabled reference count. This is useful for drivers that +/// might call enable and disable to manage the `enable` reference count at +/// runtime, perhaps as a result of `open()` and `close()` calls or whatever +/// other driver-specific or subsystem-specific hooks. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Dynamic}; +/// struct PrivateData { +/// regulator: Regulator, +/// } +/// +/// // A fictictious probe function that obtains a regulator and sets it up. +/// fn probe(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let mut regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// Ok(PrivateData { regulator }) +/// } +/// +/// // A fictictious function that indicates that the device is going to be used. +/// fn open(dev: &Device, data: &mut PrivateData) -> Result { +/// // Increase the `enabled` reference count. +/// data.regulator.enable()?; +/// +/// Ok(()) +/// } +/// +/// fn close(dev: &Device, data: &mut PrivateData) -> Result { +/// // Decrease the `enabled` reference count. +/// data.regulator.disable()?; +/// +/// Ok(()) +/// } +/// +/// fn remove(dev: &Device, data: PrivateData) -> Result { +/// // `PrivateData` is dropped here, which will drop the +/// // `Regulator` in turn. +/// // +/// // The reference that was obtained by `regulator_get()` will be +/// // released, but it is up to the user to make sure that the number of calls +/// // to `enable()` and `disabled()` are balanced before this point. +/// Ok(()) +/// } +/// ``` +/// +/// # Invariants +/// +/// - `inner` is a non-null wrapper over a pointer to a `struct +/// regulator` obtained from [`regulator_get()`]. +/// +/// [`regulator_get()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_get +pub struct Regulator +where + State: RegulatorState, +{ + inner: NonNull, + _phantom: PhantomData, +} + +impl Regulator { + /// Sets the voltage for the regulator. + /// + /// This can be used to ensure that the device powers up cleanly. + pub fn set_voltage(&self, min_voltage: Voltage, max_voltage: Voltage) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { + bindings::regulator_set_voltage( + self.inner.as_ptr(), + min_voltage.as_microvolts(), + max_voltage.as_microvolts(), + ) + }) + } + + /// Gets the current voltage of the regulator. + pub fn get_voltage(&self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + let voltage = unsafe { bindings::regulator_get_voltage(self.inner.as_ptr()) }; + if voltage < 0 { + Err(kernel::error::Error::from_errno(voltage)) + } else { + Ok(Voltage::from_microvolts(voltage)) + } + } + + fn get_internal(dev: &Device, name: &CStr) -> Result> { + // SAFETY: It is safe to call `regulator_get()`, on a device pointer + // received from the C code. + let inner = from_err_ptr(unsafe { bindings::regulator_get(dev.as_raw(), name.as_ptr()) })?; + + // SAFETY: We can safely trust `inner` to be a pointer to a valid + // regulator if `ERR_PTR` was not returned. + let inner = unsafe { NonNull::new_unchecked(inner) }; + + Ok(Self { + inner, + _phantom: PhantomData, + }) + } + + fn enable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) }) + } + + fn disable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Attempts to convert the regulator to an enabled state. + pub fn try_into_enabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count to + // `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .enable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system and enables it. + /// + /// This is equivalent to calling `regulator_get_enable()` in the C API. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::::get_internal(dev, name)? + .try_into_enabled() + .map_err(|error| error.error) + } + + /// Attempts to convert the regulator to a disabled state. + pub fn try_into_disabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count + // to `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .disable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. The current state of + /// the regulator is unknown and it is up to the user to manage the enabled + /// reference count. + /// + /// This closely mimics the behavior of the C API and can be used to + /// dynamically manage the enabled reference count at runtime. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Increases the `enabled` reference count. + pub fn enable(&mut self) -> Result { + self.enable_internal() + } + + /// Decreases the `enabled` reference count. + pub fn disable(&mut self) -> Result { + self.disable_internal() + } +} + +impl Regulator { + /// Checks if the regulator is enabled. + pub fn is_enabled(&self) -> bool { + // SAFETY: Safe as per the type invariants of `Regulator`. + unsafe { bindings::regulator_is_enabled(self.inner.as_ptr()) != 0 } + } +} + +impl Drop for Regulator { + fn drop(&mut self) { + if T::DISABLE_ON_DROP { + // SAFETY: By the type invariants, we know that `self` owns a + // reference on the enabled refcount, so it is safe to relinquish it + // now. + unsafe { bindings::regulator_disable(self.inner.as_ptr()) }; + } + // SAFETY: By the type invariants, we know that `self` owns a reference, + // so it is safe to relinquish it now. + unsafe { bindings::regulator_put(self.inner.as_ptr()) }; + } +} + +/// A voltage. +/// +/// This type represents a voltage value in microvolts. +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Voltage(i32); + +impl Voltage { + /// Creates a new `Voltage` from a value in microvolts. + pub fn from_microvolts(uv: i32) -> Self { + Self(uv) + } + + /// Returns the value of the voltage in microvolts as an [`i32`]. + pub fn as_microvolts(self) -> i32 { + self.0 + } +} -- GitLab From d9f334fca5448907cc47ba8553926f9ba148512f Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 14 Jul 2025 15:52:05 -0300 Subject: [PATCH 727/868] MAINTAINERS: add regulator.rs to the regulator API entry Add this file to the regulator API entry as requested by Mark Brown. Signed-off-by: Daniel Almeida Acked-by: Miguel Ojeda Link: https://patch.msgid.link/20250714-topics-tyr-regulator2-v8-2-c7ab3955d524@collabora.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 60bba48f5479a..09ff9ae60e6f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26561,6 +26561,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git F: Documentation/devicetree/bindings/regulator/ F: Documentation/power/regulator/ F: drivers/regulator/ +F: rust/kernel/regulator.rs F: include/dt-bindings/regulator/ F: include/linux/regulator/ K: regulator_get_optional -- GitLab From 2aa8ccab5ae67281e4d3660f8d9ee68d8b76fcef Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Mon, 14 Jul 2025 09:37:30 -0400 Subject: [PATCH 728/868] gpio: pca953x: use regmap_update_bits() to improve performance Using regmap_update_bits() allows to reduce the number of I2C transfers when updating bits that haven't changed on non-volatile registers. For example on a PCAL6416, when changing a GPIO direction from input to output, the number of I2C transfers can be reduced from 4 to just 1 if the pull resistors configuration hasn't changed and the output value is the same as before. Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20250714133730.6353-1-hugo@hugovil.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-pca953x.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f5184860bd89f..169877e59da9d 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -611,9 +611,9 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) guard(mutex)(&chip->i2c_lock); if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) - return regmap_write_bits(chip->regmap, dirreg, bit, 0); + return regmap_update_bits(chip->regmap, dirreg, bit, 0); - return regmap_write_bits(chip->regmap, dirreg, bit, bit); + return regmap_update_bits(chip->regmap, dirreg, bit, bit); } static int pca953x_gpio_direction_output(struct gpio_chip *gc, @@ -628,7 +628,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, guard(mutex)(&chip->i2c_lock); /* set output level */ - ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + ret = regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); if (ret) return ret; @@ -637,9 +637,9 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, * (in/out logic is inverted on TCA6418) */ if (PCA_CHIP_TYPE(chip->driver_data) == TCA6418_TYPE) - return regmap_write_bits(chip->regmap, dirreg, bit, bit); + return regmap_update_bits(chip->regmap, dirreg, bit, bit); - return regmap_write_bits(chip->regmap, dirreg, bit, 0); + return regmap_update_bits(chip->regmap, dirreg, bit, 0); } static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) @@ -667,7 +667,7 @@ static int pca953x_gpio_set_value(struct gpio_chip *gc, unsigned int off, guard(mutex)(&chip->i2c_lock); - return regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0); + return regmap_update_bits(chip->regmap, outreg, bit, val ? bit : 0); } static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) @@ -751,9 +751,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Configure pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_PULL_UP) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, bit); else if (param == PIN_CONFIG_BIAS_PULL_DOWN) - ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + ret = regmap_update_bits(chip->regmap, pull_sel_reg, bit, 0); else ret = 0; if (ret) @@ -761,9 +761,9 @@ static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, /* Disable/Enable pull-up/pull-down */ if (param == PIN_CONFIG_BIAS_DISABLE) - return regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, 0); else - return regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + return regmap_update_bits(chip->regmap, pull_en_reg, bit, bit); } static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, -- GitLab From 859d97606f032d10d4633e17541f07f1f355e282 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jul 2025 09:51:21 +0200 Subject: [PATCH 729/868] arm: multi_v7_defconfig: Update HD-audio configs Since the reorganization of HD-audio drivers, Realtek and HDMI codec drivers have been split. Update to the new Kconfigs to catch up. Fixes: aeeb85f26c3b ("ALSA: hda: Split Realtek HD-audio codec driver") Fixes: 73cd0490819d ("ALSA: hda/hdmi: Split vendor codec drivers") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/20250715170422.5162c666@canb.auug.org.au Link: https://patch.msgid.link/20250715075237.28476-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- arch/arm/configs/multi_v7_defconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 50c170b4619f7..02ddd7ce9e3ed 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -791,8 +791,11 @@ CONFIG_SND=m CONFIG_SND_HDA_TEGRA=m CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y -CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_REALTEK_LIB=m +CONFIG_SND_HDA_CODEC_ALC269=m CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_HDMI_TEGRA=m CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=m CONFIG_SND_ATMEL_SOC=m -- GitLab From f261196d4bf1d413383eb1667e6eb199ff9af875 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jul 2025 09:51:22 +0200 Subject: [PATCH 730/868] mips: loongson3_defconfig: Update HD-audio configs Since the reorganization of HD-audio drivers, Realtek driver has been split. Update to the new Kconfigs to catch up. Fixes: aeeb85f26c3b ("ALSA: hda: Split Realtek HD-audio codec driver") Link: https://patch.msgid.link/20250715075237.28476-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- arch/mips/configs/loongson3_defconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/mips/configs/loongson3_defconfig b/arch/mips/configs/loongson3_defconfig index 98844b457b7f4..5ff0c15541687 100644 --- a/arch/mips/configs/loongson3_defconfig +++ b/arch/mips/configs/loongson3_defconfig @@ -292,7 +292,9 @@ CONFIG_SND_SEQ_DUMMY=m # CONFIG_SND_ISA is not set CONFIG_SND_HDA_INTEL=m CONFIG_SND_HDA_PATCH_LOADER=y -CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_CODEC_REALTEK_LIB=m +CONFIG_SND_HDA_CODEC_ALC269=m CONFIG_SND_HDA_CODEC_SIGMATEL=m CONFIG_SND_HDA_CODEC_HDMI=m CONFIG_SND_HDA_CODEC_CONEXANT=m -- GitLab From 3f2e4c11925ee24a34853b0d608ab85df2430555 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2025 16:17:21 +0100 Subject: [PATCH 731/868] ASoC: SDCA: Fix off by one error in IRQ bound check Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202507150415.M1tCgi3p-lkp@intel.com/ Fixes: b126394d9ec6 ("ASoC: SDCA: Generic interrupt support") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20250715151723.2964336-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 2f85fcc6e5446..eb9e7eff4b4ba 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -262,7 +262,7 @@ int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info, { int ret; - if (sdca_irq < 0 || sdca_irq > SDCA_MAX_INTERRUPTS) { + if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS) { dev_err(dev, "bad irq request: %d\n", sdca_irq); return -EINVAL; } -- GitLab From 71562278a189af2ca202eafa0ab71a9b68469207 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2025 16:17:22 +0100 Subject: [PATCH 732/868] ASoC: SDCA: Avoid use of uninitialised local name variable The local name variable is accidentally left over from an earlier version of the code. Remove the variable and its uninitialised usage. Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202507150415.M1tCgi3p-lkp@intel.com/ Fixes: b126394d9ec6 ("ASoC: SDCA: Generic interrupt support") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20250715151723.2964336-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index eb9e7eff4b4ba..b76512732af87 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -342,7 +342,6 @@ int sdca_irq_populate(struct sdca_function_data *function, int irq = control->interrupt_position; struct sdca_interrupt *interrupt; irq_handler_t handler; - const char *name; int ret; if (irq == SDCA_NO_INTERRUPT) { @@ -385,7 +384,7 @@ int sdca_irq_populate(struct sdca_function_data *function, handler, interrupt); if (ret) { dev_err(dev, "failed to request irq %s: %d\n", - name, ret); + interrupt->name, ret); return ret; } } -- GitLab From 15247b5a63f506125360fa45d7aa1fbe8b903b95 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2025 16:17:23 +0100 Subject: [PATCH 733/868] ASoC: SDCA: Update memory allocations to zero initialise All the memory allocations in the SDCA ASoC helpers rely on fields being zero initialised, the code should use kzalloc not kmalloc. Reported-by: Shuming Fan Fixes: 2c8b3a8e6aa8 ("ASoC: SDCA: Create DAPM widgets and routes from DisCo") Fixes: c3ca24e3fcb6 ("ASoC: SDCA: Create ALSA controls from DisCo") Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20250715151723.2964336-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index dd7b19083c85f..ebb1c87facf9c 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -230,11 +230,11 @@ static int entity_early_parse_ge(struct device *dev, if (!control_name) return -ENOMEM; - kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL); if (!soc_enum) return -ENOMEM; @@ -561,11 +561,11 @@ static int entity_parse_su_class(struct device *dev, const char **texts; int i; - kctl = devm_kmalloc(dev, sizeof(*kctl), GFP_KERNEL); + kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - soc_enum = devm_kmalloc(dev, sizeof(*soc_enum), GFP_KERNEL); + soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL); if (!soc_enum) return -ENOMEM; @@ -672,7 +672,7 @@ static int entity_parse_mu(struct device *dev, if (!control_name) return -ENOMEM; - mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; @@ -926,7 +926,7 @@ static int populate_control(struct device *dev, if (!control_name) return -ENOMEM; - mc = devm_kmalloc(dev, sizeof(*mc), GFP_KERNEL); + mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; -- GitLab From 4eb6ad5d2080681b531db2c1764246f9a868062f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:49 +0100 Subject: [PATCH 734/868] ASoC: SDCA: Allow read-only controls to be deferrable The current SDCA Control parsing only checks the deferrable flag for Read/Write and Dual Ranked controls. However, reads can defer as well as writes so Read Only controls should also check for the deferrable flag. Add the handling for this into find_sdca_entity_control(). Fixes: 42b144cb6a2d ("ASoC: SDCA: Add SDCA Control parsing") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index be09b7a341027..7392ddcdd4831 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -881,7 +881,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti control->value = tmp; control->has_fixed = true; } - + fallthrough; + case SDCA_ACCESS_MODE_RO: control->deferrable = fwnode_property_read_bool(control_node, "mipi-sdca-control-deferrable"); break; -- GitLab From cbcb5f5c2be523ef0908df290b3033138bd4c185 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:50 +0100 Subject: [PATCH 735/868] ASoC: SDCA: Remove overly chatty input pin list warning An input pin list is not generally required, so a warning on the absence of one is a little extreme, remove this warning message. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 7392ddcdd4831..e567e364dc310 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1635,7 +1635,6 @@ static int find_sdca_entity_connection(struct device *dev, ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list); if (ret == -EINVAL) { /* Allow missing pin lists, assume no pins. */ - dev_warn(dev, "%s: missing pin list\n", entity->label); return 0; } else if (ret) { dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret); -- GitLab From c57ad862462f064c0bd943a5828f5e0eca469ca5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:51 +0100 Subject: [PATCH 736/868] ASoC: SDCA: Move SDCA search functions and export The ASoC code for SDCA contains several helper functions that search for controls/ranges/etc. As the code evolves these helpers are likely to be useful to anything interacting with the stored DisCo data. Move the helpers into sdca_function.c and export them so other modules can also use them. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 11 +++++ sound/soc/sdca/sdca_asoc.c | 73 ++++++--------------------------- sound/soc/sdca/sdca_functions.c | 50 ++++++++++++++++++++++ 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index b4a97ff087294..543c09e99ab1c 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1316,4 +1316,15 @@ int sdca_parse_function(struct device *dev, struct sdca_function_desc *desc, struct sdca_function_data *function); +struct sdca_control *sdca_selector_find_control(struct device *dev, + struct sdca_entity *entity, + const int sel); +struct sdca_control_range *sdca_control_find_range(struct device *dev, + struct sdca_entity *entity, + struct sdca_control *control, + int cols, int rows); +struct sdca_control_range *sdca_selector_find_range(struct device *dev, + struct sdca_entity *entity, + int sel, int cols, int rows); + #endif diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index dd7b19083c85f..11c9b3b935fc9 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -26,53 +26,6 @@ #include #include -static struct sdca_control *selector_find_control(struct device *dev, - struct sdca_entity *entity, - const int sel) -{ - int i; - - for (i = 0; i < entity->num_controls; i++) { - struct sdca_control *control = &entity->controls[i]; - - if (control->sel == sel) - return control; - } - - dev_err(dev, "%s: control %#x: missing\n", entity->label, sel); - return NULL; -} - -static struct sdca_control_range *control_find_range(struct device *dev, - struct sdca_entity *entity, - struct sdca_control *control, - int cols, int rows) -{ - struct sdca_control_range *range = &control->range; - - if ((cols && range->cols != cols) || (rows && range->rows != rows) || - !range->data) { - dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n", - entity->label, control->sel, range->cols, range->rows); - return NULL; - } - - return range; -} - -static struct sdca_control_range *selector_find_range(struct device *dev, - struct sdca_entity *entity, - int sel, int cols, int rows) -{ - struct sdca_control *control; - - control = selector_find_control(dev, entity, sel); - if (!control) - return NULL; - - return control_find_range(dev, entity, control, cols, rows); -} - static bool exported_control(struct sdca_entity *entity, struct sdca_control *control) { switch (SDCA_CTL_TYPE(entity->type, control->sel)) { @@ -213,7 +166,7 @@ static int entity_early_parse_ge(struct device *dev, const char **texts; int i; - control = selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE); if (!control) return -EINVAL; @@ -221,7 +174,7 @@ static int entity_early_parse_ge(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - range = control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0); + range = sdca_control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0); if (!range) return -EINVAL; @@ -443,7 +396,7 @@ static int entity_parse_pde(struct device *dev, unsigned int mask = 0; int i; - control = selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS); if (!control) return -EINVAL; @@ -452,7 +405,7 @@ static int entity_parse_pde(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - range = control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0); + range = sdca_control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0); if (!range) return -EINVAL; @@ -499,8 +452,8 @@ static int entity_parse_su_device(struct device *dev, return -EINVAL; } - range = selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE, - SDCA_SELECTED_MODE_NCOLS, 0); + range = sdca_selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE, + SDCA_SELECTED_MODE_NCOLS, 0); if (!range) return -EINVAL; @@ -613,7 +566,7 @@ static int entity_parse_su(struct device *dev, return -EINVAL; } - control = selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR); if (!control) return -EINVAL; @@ -643,7 +596,7 @@ static int entity_parse_mu(struct device *dev, return -EINVAL; } - control = selector_find_control(dev, entity, SDCA_CTL_MU_MIXER); + control = sdca_selector_find_control(dev, entity, SDCA_CTL_MU_MIXER); if (!control) return -EINVAL; @@ -853,7 +806,7 @@ static int control_limit_kctl(struct device *dev, /* * FIXME: For now only handle the simple case of a single linear range */ - range = control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1); + range = sdca_control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1); if (!range) return -EINVAL; @@ -1140,9 +1093,9 @@ static int populate_rate_format(struct device *dev, } if (entity->iot.clock) { - range = selector_find_range(dev, entity->iot.clock, - SDCA_CTL_CS_SAMPLERATEINDEX, - SDCA_SAMPLERATEINDEX_NCOLS, 0); + range = sdca_selector_find_range(dev, entity->iot.clock, + SDCA_CTL_CS_SAMPLERATEINDEX, + SDCA_SAMPLERATEINDEX_NCOLS, 0); if (!range) return -EINVAL; @@ -1154,7 +1107,7 @@ static int populate_rate_format(struct device *dev, clock_rates = UINT_MAX; } - range = selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); + range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); if (!range) return -EINVAL; diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index e567e364dc310..d2e2c8d10b92a 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1941,5 +1941,55 @@ int sdca_parse_function(struct device *dev, } EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); +struct sdca_control *sdca_selector_find_control(struct device *dev, + struct sdca_entity *entity, + const int sel) +{ + int i; + + for (i = 0; i < entity->num_controls; i++) { + struct sdca_control *control = &entity->controls[i]; + + if (control->sel == sel) + return control; + } + + dev_err(dev, "%s: control %#x: missing\n", entity->label, sel); + return NULL; +} +EXPORT_SYMBOL_NS(sdca_selector_find_control, "SND_SOC_SDCA"); + +struct sdca_control_range *sdca_control_find_range(struct device *dev, + struct sdca_entity *entity, + struct sdca_control *control, + int cols, int rows) +{ + struct sdca_control_range *range = &control->range; + + if ((cols && range->cols != cols) || (rows && range->rows != rows) || + !range->data) { + dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n", + entity->label, control->sel, range->cols, range->rows); + return NULL; + } + + return range; +} +EXPORT_SYMBOL_NS(sdca_control_find_range, "SND_SOC_SDCA"); + +struct sdca_control_range *sdca_selector_find_range(struct device *dev, + struct sdca_entity *entity, + int sel, int cols, int rows) +{ + struct sdca_control *control; + + control = sdca_selector_find_control(dev, entity, sel); + if (!control) + return NULL; + + return sdca_control_find_range(dev, entity, control, cols, rows); +} +EXPORT_SYMBOL_NS(sdca_selector_find_range, "SND_SOC_SDCA"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); -- GitLab From 5f86d41d0410b072b5f4875ef5d38bf8d18eed55 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:52 +0100 Subject: [PATCH 737/868] ASoC: soc-dai: Add private data to snd_soc_dai Add a private data pointer that can be used to store context along with the DAI. This will be useful to allow the SDCA class library to store data separately from the CODEC driver itself. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index d19ab5572d2ba..166c29557e9d7 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -463,6 +463,9 @@ struct snd_soc_dai { /* bit field */ unsigned int probed:1; + + /* DAI private data */ + void *priv; }; static inline const struct snd_soc_pcm_stream * -- GitLab From 7b0d60dbb468fa82e9053292cdc8a5436400bfaf Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:53 +0100 Subject: [PATCH 738/868] ASoC: SDCA: Add helper to add DAI constraints Currently the core SDCA code simply creates a place holder available channels from 1 to SDCA_MAX_CHANNEL_COUNT. Add a helper function that will constrain the number of channels based on the actual available SDCA Clusters in DisCo. Currently this code only handles Input Terminal Entities as they directly specify the Cluster. More work will be required later for Output Terminals which inherit their Cluster. Typically this new helper would be called from the DAIs startup callback. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 10 ++++ include/sound/sdca_function.h | 12 ++++ sound/soc/sdca/sdca_asoc.c | 99 +++++++++++++++++++++++++++++++++ sound/soc/sdca/sdca_functions.c | 18 ++++++ 4 files changed, 139 insertions(+) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index 9121531f08260..bbf146e4fcea7 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -11,9 +11,12 @@ #define __SDCA_ASOC_H__ struct device; +struct regmap; struct sdca_function_data; struct snd_kcontrol_new; +struct snd_pcm_substream; struct snd_soc_component_driver; +struct snd_soc_dai; struct snd_soc_dai_driver; struct snd_soc_dai_ops; struct snd_soc_dapm_route; @@ -39,4 +42,11 @@ int sdca_asoc_populate_component(struct device *dev, struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, const struct snd_soc_dai_ops *ops); +int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + #endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 543c09e99ab1c..3bde07409bf36 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1268,6 +1268,15 @@ struct sdca_cluster { struct sdca_channel *channels; }; +/** + * enum sdca_cluster_range - SDCA Range column definitions for ClusterIndex + */ +enum sdca_cluster_range { + SDCA_CLUSTER_BYTEINDEX = 0, + SDCA_CLUSTER_CLUSTERID = 1, + SDCA_CLUSTER_NCOLS = 2, +}; + /** * struct sdca_function_data - top-level information for one SDCA function * @desc: Pointer to short descriptor from initial parsing. @@ -1326,5 +1335,8 @@ struct sdca_control_range *sdca_control_find_range(struct device *dev, struct sdca_control_range *sdca_selector_find_range(struct device *dev, struct sdca_entity *entity, int sel, int cols, int rows); +struct sdca_cluster *sdca_id_find_cluster(struct device *dev, + struct sdca_function_data *function, + const int id); #endif diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 11c9b3b935fc9..1a01492875841 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -7,16 +7,20 @@ * https://www.mipi.org/mipi-sdca-v1-0-download */ +#include #include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -1269,3 +1273,98 @@ int sdca_asoc_populate_component(struct device *dev, return 0; } EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA"); + +/** + * sdca_asoc_set_constraints - constrain channels available on a DAI + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @substream: Pointer to the PCM substream. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from startup(). + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + static const unsigned int channel_list[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + }; + struct sdca_entity *entity = &function->entities[dai->id]; + struct snd_pcm_hw_constraint_list *constraint; + struct sdca_control_range *range; + struct sdca_control *control; + unsigned int channel_mask = 0; + int i, ret; + + static_assert(ARRAY_SIZE(channel_list) == SDCA_MAX_CHANNEL_COUNT); + static_assert(sizeof(channel_mask) * BITS_PER_BYTE >= SDCA_MAX_CHANNEL_COUNT); + + if (entity->type != SDCA_ENTITY_TYPE_IT) + return 0; + + control = sdca_selector_find_control(dev, entity, SDCA_CTL_IT_CLUSTERINDEX); + if (!control) + return -EINVAL; + + range = sdca_control_find_range(dev, entity, control, SDCA_CLUSTER_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + int clusterid = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); + struct sdca_cluster *cluster; + + cluster = sdca_id_find_cluster(dev, function, clusterid); + if (!cluster) + return -ENODEV; + + channel_mask |= (1 << (cluster->num_channels - 1)); + } + + dev_dbg(dev, "%s: set channel constraint mask: %#x\n", + entity->label, channel_mask); + + constraint = kzalloc(sizeof(*constraint), GFP_KERNEL); + if (!constraint) + return -ENOMEM; + + constraint->count = ARRAY_SIZE(channel_list); + constraint->list = channel_list; + constraint->mask = channel_mask; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + constraint); + if (ret) { + dev_err(dev, "%s: failed to add constraint: %d\n", entity->label, ret); + kfree(constraint); + return ret; + } + + dai->priv = constraint; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA"); + +/** + * sdca_asoc_free_constraints - free constraint allocations + * @substream: Pointer to the PCM substream. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from shutdown(). + */ +void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_hw_constraint_list *constraint = dai->priv; + + kfree(constraint); +} +EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index d2e2c8d10b92a..4b6da587c4ac3 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1991,5 +1991,23 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, } EXPORT_SYMBOL_NS(sdca_selector_find_range, "SND_SOC_SDCA"); +struct sdca_cluster *sdca_id_find_cluster(struct device *dev, + struct sdca_function_data *function, + const int id) +{ + int i; + + for (i = 0; i < function->num_clusters; i++) { + struct sdca_cluster *cluster = &function->clusters[i]; + + if (cluster->id == id) + return cluster; + } + + dev_err(dev, "%s: cluster %#x: missing\n", function->desc->name, id); + return NULL; +} +EXPORT_SYMBOL_NS(sdca_id_find_cluster, "SND_SOC_SDCA"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); -- GitLab From 264d3d776fb1a428706b0ca0f679bbed876fe7c9 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:54 +0100 Subject: [PATCH 739/868] ASoC: SDCA: Add a helper to get the SoundWire port number Add a helper function to extract the SoundWire hardware port number from the SDCA DataPort Selector Control. Typically this would be called from hw_params() and used to call sdw_stream_add_slave(). Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 3 ++ include/sound/sdca_function.h | 8 ++++ sound/soc/sdca/sdca_asoc.c | 75 +++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index bbf146e4fcea7..800a26adcd8e0 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -48,5 +48,8 @@ int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, struct snd_soc_dai *dai); void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_soc_dai *dai); #endif // __SDCA_ASOC_H__ diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index 3bde07409bf36..90d77fc46416b 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -185,6 +185,14 @@ enum sdca_usage_range { SDCA_USAGE_NCOLS = 7, }; +/** + * enum sdca_dataport_selector_range - Column definitions for DataPort_Selector + */ +enum sdca_dataport_selector_range { + SDCA_DATAPORT_SELECTOR_NCOLS = 16, + SDCA_DATAPORT_SELECTOR_NROWS = 4, +}; + /** * enum sdca_mu_controls - SDCA Controls for Mixer Unit * diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 1a01492875841..03c663413cc9d 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1368,3 +1369,77 @@ void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, kfree(constraint); } EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA"); + +/** + * sdca_asoc_get_port - return SoundWire port for a DAI + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from hw_params(). + * + * Return: Returns a positive port number on success, and a negative error + * code on failure. + */ +int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_soc_dai *dai) +{ + struct sdca_entity *entity = &function->entities[dai->id]; + struct sdca_control_range *range; + unsigned int reg, val; + int sel = -EINVAL; + int i, ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + sel = SDCA_CTL_IT_DATAPORT_SELECTOR; + break; + case SDCA_ENTITY_TYPE_OT: + sel = SDCA_CTL_OT_DATAPORT_SELECTOR; + break; + default: + break; + } + + if (sel < 0 || !entity->iot.is_dataport) { + dev_err(dev, "%s: port number only available for dataports\n", + entity->label); + return -EINVAL; + } + + range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS, + SDCA_DATAPORT_SELECTOR_NROWS); + if (!range) + return -EINVAL; + + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0); + + ret = regmap_read(regmap, reg, &val); + if (ret) { + dev_err(dev, "%s: failed to read dataport selector: %d\n", + entity->label, ret); + return ret; + } + + for (i = 0; i < range->rows; i++) { + static const u8 port_mask = 0xF; + + sel = sdca_range(range, val & port_mask, i); + + /* + * FIXME: Currently only a single dataport is supported, so + * return the first one found, technically up to 4 dataports + * could be linked, but this is not yet supported. + */ + if (sel != 0xFF) + return sel; + + val >>= hweight8(port_mask); + } + + dev_err(dev, "%s: no dataport found\n", entity->label); + return -ENODEV; +} +EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA"); -- GitLab From 4ed357f72a0e0a691304e5f14a3323811c8ce862 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 7 Jul 2025 13:41:55 +0100 Subject: [PATCH 740/868] ASoC: SDCA: Add hw_params() helper function Add a helper function that can be called from hw_params() in the DAI ops to configure the SDCA Cluster, Clock and Usage controls. These setup the channels, sample rate, and bit depths that will be used by the Terminal. Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20250707124155.2596744-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 6 ++ sound/soc/sdca/sdca_asoc.c | 180 +++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index 800a26adcd8e0..aa9124f932189 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -14,6 +14,7 @@ struct device; struct regmap; struct sdca_function_data; struct snd_kcontrol_new; +struct snd_pcm_hw_params; struct snd_pcm_substream; struct snd_soc_component_driver; struct snd_soc_dai; @@ -51,5 +52,10 @@ void sdca_asoc_free_constraints(struct snd_pcm_substream *substream, int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, struct sdca_function_data *function, struct snd_soc_dai *dai); +int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); #endif // __SDCA_ASOC_H__ diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 03c663413cc9d..252d723770914 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1443,3 +1444,182 @@ int sdca_asoc_get_port(struct device *dev, struct regmap *regmap, return -ENODEV; } EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA"); + +static int set_cluster(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, unsigned int channels) +{ + int sel = SDCA_CTL_IT_CLUSTERINDEX; + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_CLUSTER_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + int cluster_id = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i); + struct sdca_cluster *cluster; + + cluster = sdca_id_find_cluster(dev, function, cluster_id); + if (!cluster) + return -ENODEV; + + if (cluster->num_channels == channels) { + int index = sdca_range(range, SDCA_CLUSTER_BYTEINDEX, i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, index); + if (ret) { + dev_err(dev, "%s: failed to write cluster index: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set cluster to %d (%d channels)\n", + entity->label, index, channels); + + return 0; + } + } + + dev_err(dev, "%s: no cluster for %d channels\n", entity->label, channels); + return -EINVAL; +} + +static int set_clock(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, int target_rate) +{ + int sel = SDCA_CTL_CS_SAMPLERATEINDEX; + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_SAMPLERATEINDEX_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + unsigned int rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i); + + if (rate == target_rate) { + unsigned int index = sdca_range(range, + SDCA_SAMPLERATEINDEX_INDEX, + i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, index); + if (ret) { + dev_err(dev, "%s: failed to write clock rate: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set clock rate to %d (%dHz)\n", + entity->label, index, rate); + + return 0; + } + } + + dev_err(dev, "%s: no clock rate for %dHz\n", entity->label, target_rate); + return -EINVAL; +} + +static int set_usage(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct sdca_entity *entity, int sel, + int target_rate, int target_width) +{ + struct sdca_control_range *range; + int i, ret; + + range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0); + if (!range) + return -EINVAL; + + for (i = 0; i < range->rows; i++) { + unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i); + unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i); + + if ((!rate || rate == target_rate) && width == target_width) { + unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i); + unsigned int reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, sel, 0); + + ret = regmap_update_bits(regmap, reg, 0xFF, usage); + if (ret) { + dev_err(dev, "%s: failed to write usage: %d\n", + entity->label, ret); + return ret; + } + + dev_dbg(dev, "%s: set usage to %#x (%dHz, %d bits)\n", + entity->label, usage, target_rate, target_width); + + return 0; + } + } + + dev_err(dev, "%s: no usage for %dHz, %dbits\n", + entity->label, target_rate, target_width); + return -EINVAL; +} + +/** + * sdca_asoc_hw_params - set SDCA channels, sample rate and bit depth + * @dev: Pointer to the device, used for error messages. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information. + * @substream: Pointer to the PCM substream. + * @params: Pointer to the hardware parameters. + * @dai: Pointer to the ASoC DAI. + * + * Typically called from hw_params(). + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdca_entity *entity = &function->entities[dai->id]; + int channels = params_channels(params); + int width = params_width(params); + int rate = params_rate(params); + int usage_sel; + int ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + ret = set_cluster(dev, regmap, function, entity, channels); + if (ret) + return ret; + + usage_sel = SDCA_CTL_IT_USAGE; + break; + case SDCA_ENTITY_TYPE_OT: + usage_sel = SDCA_CTL_OT_USAGE; + break; + default: + dev_err(dev, "%s: hw_params on non-terminal entity\n", entity->label); + return -EINVAL; + } + + if (entity->iot.clock) { + ret = set_clock(dev, regmap, function, entity->iot.clock, rate); + if (ret) + return ret; + } + + ret = set_usage(dev, regmap, function, entity, usage_sel, rate, width); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_asoc_hw_params, "SND_SOC_SDCA"); -- GitLab From d5255ae7ec48ac1f702e95b472801dbb7bf1e97f Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Tue, 15 Jul 2025 15:27:10 -0500 Subject: [PATCH 741/868] spi: dt-bindings: spi-mux: Drop "spi-max-frequency" as required There's little reason to require the SPI mux to define a maximum bus frequency as the muxing is just the chip select and devices still define their maximum freq. In fact, several users don't set "spi-max-frequency" which caused warnings. Signed-off-by: Rob Herring (Arm) Reviewed-by: Chris Packham Link: https://patch.msgid.link/20250715202711.1882103-1-robh@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mux.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/spi-mux.yaml b/Documentation/devicetree/bindings/spi/spi-mux.yaml index fb2a6039928c4..b1e2a97be6995 100644 --- a/Documentation/devicetree/bindings/spi/spi-mux.yaml +++ b/Documentation/devicetree/bindings/spi/spi-mux.yaml @@ -46,7 +46,6 @@ properties: required: - compatible - reg - - spi-max-frequency - mux-controls unevaluatedProperties: false -- GitLab From 03d4bd5729f3adb3dbf923ab08affb5bad262d53 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 15 Jul 2025 10:19:44 +0200 Subject: [PATCH 742/868] gpio: wcove: use regmap_assign_bits() in .set() Replace the if-else with a direct call to the regmap_assign_bits() helper and save a couple lines of code. Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250715-gpiochip-set-rv-gpio-remaining-v2-1-072b4cf06330@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wcove.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 1ec24f6f9300f..816fb8d113e66 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -208,10 +208,7 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) if (reg < 0) return; - if (value) - regmap_set_bits(wg->regmap, reg, 1); - else - regmap_clear_bits(wg->regmap, reg, 1); + regmap_assign_bits(wg->regmap, reg, 1, value); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, -- GitLab From 26b6443826d98ac1f5c780788549b8df30e12fa2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 15 Jul 2025 10:19:45 +0200 Subject: [PATCH 743/868] gpio: wcove: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250715-gpiochip-set-rv-gpio-remaining-v2-2-072b4cf06330@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-wcove.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 816fb8d113e66..f7df3d5fc71c1 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -200,15 +200,15 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) return val & 0x1; } -static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) +static int wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); int reg = to_reg(gpio, CTRL_OUT); if (reg < 0) - return; + return 0; - regmap_assign_bits(wg->regmap, reg, 1, value); + return regmap_assign_bits(wg->regmap, reg, 1, value); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, @@ -439,7 +439,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) wg->chip.direction_output = wcove_gpio_dir_out; wg->chip.get_direction = wcove_gpio_get_direction; wg->chip.get = wcove_gpio_get; - wg->chip.set = wcove_gpio_set; + wg->chip.set_rv = wcove_gpio_set; wg->chip.set_config = wcove_gpio_set_config; wg->chip.base = -1; wg->chip.ngpio = WCOVE_VGPIO_NUM; -- GitLab From 32ad0b9a17f9aa8dd9308feda671bda98b274d24 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:48 +0200 Subject: [PATCH 744/868] gpio: sysfs: use gpiod_is_equal() to compare GPIO descriptors We have a dedicated comparator for GPIO descriptors that performs additional checks and hides the implementation detail of whether the same GPIO can be associated with two separate struct gpio_desc objects. Use it in sysfs code Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-1-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index c4c21e25c682b..c4e85f2827697 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -657,7 +657,7 @@ static int match_export(struct device *dev, const void *desc) { struct gpiod_data *data = dev_get_drvdata(dev); - return data->desc == desc; + return gpiod_is_equal(data->desc, desc); } /** -- GitLab From 2028f854b3f5b3816cd5d5dd83057a873eddc4d6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:49 +0200 Subject: [PATCH 745/868] gpio: sysfs: add a parallel class device for each GPIO chip using device IDs In order to enable moving away from the global GPIO numberspace-based exporting of lines over sysfs: add a parallel, per-chip entry under /sys/class/gpio/ for every registered GPIO chip, denoted by device ID in the file name and not its base GPIO number. Compared to the existing chip group: it does not contain the "base" attribute as the goal of this change is to not refer to GPIOs by their global number from user-space anymore. It also contains its own, per-chip export/unexport attribute pair which allow to export lines by their hardware offset within the chip. Caveat #1: the new device cannot be a link to (or be linked to by) the existing "gpiochip" entry as we cannot create links in /sys/class/xyz/. Caveat #2: the new entry cannot be named "gpiochipX" as it could conflict with devices whose base is statically defined to a low number. Let's go with "chipX" instead. While at it: the chip label is unique so update the untrue statement when extending the docs. Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-2-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- Documentation/ABI/obsolete/sysfs-gpio | 7 +- drivers/gpio/gpiolib-sysfs.c | 192 +++++++++++++++++++------- 2 files changed, 150 insertions(+), 49 deletions(-) diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index 480316fee6d80..ff694708a3bef 100644 --- a/Documentation/ABI/obsolete/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -25,8 +25,13 @@ Description: /active_low ... r/w as: 0, 1 /gpiochipN ... for each gpiochip; #N is its first GPIO /base ... (r/o) same as N - /label ... (r/o) descriptive, not necessarily unique + /label ... (r/o) descriptive chip name /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) + /chipX ... for each gpiochip; #X is the gpio device ID + /export ... asks the kernel to export a GPIO at HW offset X to userspace + /unexport ... to return a GPIO at HW offset X to the kernel + /label ... (r/o) descriptive chip name + /ngpio ... (r/o) number of GPIOs exposed by the chip This ABI is obsoleted by Documentation/ABI/testing/gpio-cdev and will be removed after 2020. diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index c4e85f2827697..990db0405cc86 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -45,6 +45,7 @@ struct gpiod_data { struct gpiodev_data { struct gpio_device *gdev; + struct device *cdev_id; /* Class device by GPIO device ID */ struct device *cdev_base; /* Class device by GPIO base */ }; @@ -399,6 +400,14 @@ static const struct attribute_group *gpio_groups[] = { * /base ... matching gpio_chip.base (N) * /label ... matching gpio_chip.label * /ngpio ... matching gpio_chip.ngpio + * + * AND + * + * /sys/class/gpio/chipX/ + * /export ... export GPIO at given offset + * /unexport ... unexport GPIO at given offset + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio */ static ssize_t base_show(struct device *dev, struct device_attribute *attr, @@ -428,6 +437,111 @@ static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(ngpio); +static int export_gpio_desc(struct gpio_desc *desc) +{ + int offset, ret; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + offset = gpio_chip_hwgpio(desc); + if (!gpiochip_line_is_valid(guard.gc, offset)) { + pr_debug_ratelimited("%s: GPIO %d masked\n", __func__, + gpio_chip_hwgpio(desc)); + return -EINVAL; + } + + /* + * No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + ret = gpiod_request_user(desc, "sysfs"); + if (ret) + return ret; + + ret = gpiod_set_transitory(desc, false); + if (ret) { + gpiod_free(desc); + return ret; + } + + ret = gpiod_export(desc, true); + if (ret < 0) { + gpiod_free(desc); + } else { + set_bit(FLAG_SYSFS, &desc->flags); + gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); + } + + return ret; +} + +static int unexport_gpio_desc(struct gpio_desc *desc) +{ + /* + * No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (!test_and_clear_bit(FLAG_SYSFS, &desc->flags)) + return -EINVAL; + + gpiod_unexport(desc); + gpiod_free(desc); + + return 0; +} + +static ssize_t do_chip_export_store(struct device *dev, + struct device_attribute *attr, + const char *buf, ssize_t size, + int (*handler)(struct gpio_desc *desc)) +{ + struct gpiodev_data *data = dev_get_drvdata(dev); + struct gpio_device *gdev = data->gdev; + struct gpio_desc *desc; + unsigned int gpio; + int ret; + + ret = kstrtouint(buf, 0, &gpio); + if (ret) + return ret; + + desc = gpio_device_get_desc(gdev, gpio); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + ret = handler(desc); + if (ret) + return ret; + + return size; +} + +static ssize_t chip_export_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return do_chip_export_store(dev, attr, buf, size, export_gpio_desc); +} + +static struct device_attribute dev_attr_export = __ATTR(export, 0200, NULL, + chip_export_store); + +static ssize_t chip_unexport_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return do_chip_export_store(dev, attr, buf, size, unexport_gpio_desc); +} + +static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200, + NULL, + chip_unexport_store); + static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, @@ -436,6 +550,15 @@ static struct attribute *gpiochip_attrs[] = { }; ATTRIBUTE_GROUPS(gpiochip); +static struct attribute *gpiochip_ext_attrs[] = { + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + &dev_attr_export.attr, + &dev_attr_unexport.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpiochip_ext); + /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) @@ -447,7 +570,7 @@ static ssize_t export_store(const struct class *class, const char *buf, size_t len) { struct gpio_desc *desc; - int status, offset; + int status; long gpio; status = kstrtol(buf, 0, &gpio); @@ -461,40 +584,7 @@ static ssize_t export_store(const struct class *class, return -EINVAL; } - CLASS(gpio_chip_guard, guard)(desc); - if (!guard.gc) - return -ENODEV; - - offset = gpio_chip_hwgpio(desc); - if (!gpiochip_line_is_valid(guard.gc, offset)) { - pr_debug_ratelimited("%s: GPIO %ld masked\n", __func__, gpio); - return -EINVAL; - } - - /* No extra locking here; FLAG_SYSFS just signifies that the - * request and export were done by on behalf of userspace, so - * they may be undone on its behalf too. - */ - - status = gpiod_request_user(desc, "sysfs"); - if (status) - goto done; - - status = gpiod_set_transitory(desc, false); - if (status) { - gpiod_free(desc); - goto done; - } - - status = gpiod_export(desc, true); - if (status < 0) { - gpiod_free(desc); - } else { - set_bit(FLAG_SYSFS, &desc->flags); - gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED); - } - -done: + status = export_gpio_desc(desc); if (status) pr_debug("%s: status %d\n", __func__, status); return status ? : len; @@ -511,7 +601,7 @@ static ssize_t unexport_store(const struct class *class, status = kstrtol(buf, 0, &gpio); if (status < 0) - goto done; + return status; desc = gpio_to_desc(gpio); /* reject bogus commands (gpiod_unexport() ignores them) */ @@ -520,18 +610,7 @@ static ssize_t unexport_store(const struct class *class, return -EINVAL; } - status = -EINVAL; - - /* No extra locking here; FLAG_SYSFS just signifies that the - * request and export were done by on behalf of userspace, so - * they may be undone on its behalf too. - */ - if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { - gpiod_unexport(desc); - gpiod_free(desc); - status = 0; - } -done: + status = unexport_gpio_desc(desc); if (status) pr_debug("%s: status %d\n", __func__, status); return status ? : len; @@ -561,6 +640,11 @@ static int match_gdev(struct device *dev, const void *desc) static struct gpiodev_data * gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) { + /* + * Find the first device in GPIO class that matches. Whether that's + * the one indexed by GPIO base or device ID doesn't matter, it has + * the same address set as driver data. + */ struct device *cdev __free(put_device) = class_find_device(&gpio_class, NULL, gdev, match_gdev); @@ -787,6 +871,17 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return err; } + data->cdev_id = device_create_with_groups(&gpio_class, parent, + MKDEV(0, 0), data, + gpiochip_ext_groups, + "chip%d", gdev->id); + if (IS_ERR(data->cdev_id)) { + device_unregister(data->cdev_base); + err = PTR_ERR(data->cdev_id); + kfree(data); + return err; + } + return 0; } @@ -802,6 +897,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) return; device_unregister(data->cdev_base); + device_unregister(data->cdev_id); kfree(data); } -- GitLab From c38c3a349b7bb994252e93c7c122fa0b50ddf12b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:50 +0200 Subject: [PATCH 746/868] gpio: sysfs: only get the dirent reference for the value attr once There's no reason to retrieve the reference to the sysfs dirent every time we request an interrupt, we can as well only do it once when exporting the GPIO. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-3-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 990db0405cc86..39e9ca31f034e 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -177,10 +177,6 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) if (data->irq < 0) return -EIO; - data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); - if (!data->value_kn) - return -ENODEV; - irq_flags = IRQF_SHARED; if (flags & GPIO_IRQF_TRIGGER_FALLING) { irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? @@ -203,7 +199,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) */ ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) - goto err_put_kn; + goto err_clr_bits; ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags, "gpiolib", data); @@ -216,10 +212,9 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) err_unlock: gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); -err_put_kn: +err_clr_bits: clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); - sysfs_put(data->value_kn); return ret; } @@ -242,7 +237,6 @@ static void gpio_sysfs_free_irq(struct device *dev) gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); clear_bit(FLAG_EDGE_RISING, &desc->flags); clear_bit(FLAG_EDGE_FALLING, &desc->flags); - sysfs_put(data->value_kn); } static const char *const trigger_names[] = { @@ -726,8 +720,16 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto err_free_data; } + data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!data->value_kn) { + status = -ENODEV; + goto err_unregister_device; + } + return 0; +err_unregister_device: + device_unregister(dev); err_free_data: kfree(data); err_clear_bit: @@ -804,6 +806,7 @@ void gpiod_unexport(struct gpio_desc *desc) data = dev_get_drvdata(dev); clear_bit(FLAG_EXPORT, &desc->flags); + sysfs_put(data->value_kn); device_unregister(dev); /* -- GitLab From 7c49c1298f3ab3331008e85ac22b2d32b4bb450f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:51 +0200 Subject: [PATCH 747/868] gpio: sysfs: pass gpiod_data directly to internal GPIO sysfs functions We don't use any fields from struct device in gpio_sysfs_request_irq(), gpio_sysfs_free_irq() and gpio_sysfs_set_active_low(). We only use the dev argument to get the associated struct gpiod_data pointer with dev_get_drvdata(). To make the transition to not using dev_get_drvdata() across line callbacks for sysfs attributes easier, pass gpiod_data directly to these functions instead of having it wrapped in struct device. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-4-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 39e9ca31f034e..0cfa73e92b47f 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -162,9 +162,8 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) } /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) +static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags) { - struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; unsigned long irq_flags; int ret; @@ -223,9 +222,8 @@ err_clr_bits: * Caller holds gpiod-data mutex (unless called after class-device * deregistration). */ -static void gpio_sysfs_free_irq(struct device *dev) +static void gpio_sysfs_free_irq(struct gpiod_data *data) { - struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; CLASS(gpio_chip_guard, guard)(desc); @@ -278,12 +276,12 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, return size; if (data->irq_flags) - gpio_sysfs_free_irq(dev); + gpio_sysfs_free_irq(data); if (!flags) return size; - status = gpio_sysfs_request_irq(dev, flags); + status = gpio_sysfs_request_irq(data, flags); if (status) return status; @@ -294,9 +292,8 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ -static int gpio_sysfs_set_active_low(struct device *dev, int value) +static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) { - struct gpiod_data *data = dev_get_drvdata(dev); unsigned int flags = data->irq_flags; struct gpio_desc *desc = data->desc; int status = 0; @@ -309,8 +306,8 @@ static int gpio_sysfs_set_active_low(struct device *dev, int value) /* reconfigure poll(2) support if enabled on one edge only */ if (flags == GPIO_IRQF_TRIGGER_FALLING || flags == GPIO_IRQF_TRIGGER_RISING) { - gpio_sysfs_free_irq(dev); - status = gpio_sysfs_request_irq(dev, flags); + gpio_sysfs_free_irq(data); + status = gpio_sysfs_request_irq(data, flags); } gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG); @@ -345,7 +342,7 @@ static ssize_t active_low_store(struct device *dev, guard(mutex)(&data->mutex); - return gpio_sysfs_set_active_low(dev, value) ?: size; + return gpio_sysfs_set_active_low(data, value) ?: size; } static DEVICE_ATTR_RW(active_low); @@ -814,7 +811,7 @@ void gpiod_unexport(struct gpio_desc *desc) * edge_store. */ if (data->irq_flags) - gpio_sysfs_free_irq(dev); + gpio_sysfs_free_irq(data); } put_device(dev); -- GitLab From 12faec7ed1793221c1dc9f69575a814528d74691 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:52 +0200 Subject: [PATCH 748/868] gpio: sysfs: rename the data variable in gpiod_(un)export() In preparation for future commits which will make use of descriptor AND GPIO-device data in the same functions rename the former from data to desc_data separately which will make future changes smaller and easier to read. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-5-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 0cfa73e92b47f..fc9c19fde3f12 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -663,8 +663,8 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { + struct gpiod_data *desc_data; struct gpio_device *gdev; - struct gpiod_data *data; struct device *dev; int status; @@ -696,29 +696,29 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto err_clear_bit; } - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { + desc_data = kzalloc(sizeof(*desc_data), GFP_KERNEL); + if (!desc_data) { status = -ENOMEM; goto err_clear_bit; } - data->desc = desc; - mutex_init(&data->mutex); + desc_data->desc = desc; + mutex_init(&desc_data->mutex); if (guard.gc->direction_input && guard.gc->direction_output) - data->direction_can_change = direction_may_change; + desc_data->direction_can_change = direction_may_change; else - data->direction_can_change = false; + desc_data->direction_can_change = false; dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), data, gpio_groups, + MKDEV(0, 0), desc_data, gpio_groups, "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto err_free_data; } - data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); - if (!data->value_kn) { + desc_data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!desc_data->value_kn) { status = -ENODEV; goto err_unregister_device; } @@ -728,7 +728,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) err_unregister_device: device_unregister(dev); err_free_data: - kfree(data); + kfree(desc_data); err_clear_bit: clear_bit(FLAG_EXPORT, &desc->flags); gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -785,7 +785,7 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *data; + struct gpiod_data *desc_data; struct device *dev; if (!desc) { @@ -801,22 +801,22 @@ void gpiod_unexport(struct gpio_desc *desc) if (!dev) return; - data = dev_get_drvdata(dev); + desc_data = dev_get_drvdata(dev); clear_bit(FLAG_EXPORT, &desc->flags); - sysfs_put(data->value_kn); + sysfs_put(desc_data->value_kn); device_unregister(dev); /* * Release irq after deregistration to prevent race with * edge_store. */ - if (data->irq_flags) - gpio_sysfs_free_irq(data); + if (desc_data->irq_flags) + gpio_sysfs_free_irq(desc_data); } put_device(dev); - mutex_destroy(&data->mutex); - kfree(data); + mutex_destroy(&desc_data->mutex); + kfree(desc_data); } EXPORT_SYMBOL_GPL(gpiod_unexport); -- GitLab From f7d4fb62d04542646a48de08b10354692f3b98ce Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:53 +0200 Subject: [PATCH 749/868] gpio: sysfs: don't use driver data in sysfs callbacks for line attributes Currently each exported GPIO is represented in sysfs as a separate class device. This allows us to simply use dev_get_drvdata() to retrieve the pointer passed to device_create_with_groups() from sysfs ops callbacks. However, we're preparing to add a parallel set of per-line sysfs attributes that will live inside the associated gpiochip group. They are not registered as class devices and so have the parent device passed as argument to their callbacks (the GPIO chip class device). Put the attribute structs inside the GPIO descriptor data and dereference the relevant ones using container_of() in the callbacks. This way, we'll be able to reuse the same code for both the legacy and new GPIO attributes. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-6-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 123 +++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 40 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fc9c19fde3f12..e10d720ee0adb 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -32,6 +32,15 @@ struct kernfs_node; #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ GPIO_IRQF_TRIGGER_RISING) +enum { + GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION = 0, + GPIO_SYSFS_LINE_CLASS_ATTR_VALUE, + GPIO_SYSFS_LINE_CLASS_ATTR_EDGE, + GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW, + GPIO_SYSFS_LINE_CLASS_ATTR_SENTINEL, + GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, +}; + struct gpiod_data { struct gpio_desc *desc; @@ -41,6 +50,15 @@ struct gpiod_data { unsigned char irq_flags; bool direction_can_change; + + struct device_attribute dir_attr; + struct device_attribute val_attr; + struct device_attribute edge_attr; + struct device_attribute active_low_attr; + + struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE]; + struct attribute_group class_attr_group; + const struct attribute_group *class_attr_groups[2]; }; struct gpiodev_data { @@ -79,7 +97,8 @@ static DEFINE_MUTEX(sysfs_lock); static ssize_t direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; int value; @@ -95,7 +114,8 @@ static ssize_t direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; ssize_t status; @@ -112,12 +132,12 @@ static ssize_t direction_store(struct device *dev, return status ? : size; } -static DEVICE_ATTR_RW(direction); static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; ssize_t status; @@ -133,7 +153,8 @@ static ssize_t value_show(struct device *dev, struct device_attribute *attr, static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; ssize_t status; long value; @@ -150,7 +171,6 @@ static ssize_t value_store(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { @@ -247,7 +267,8 @@ static const char *const trigger_names[] = { static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); int flags; scoped_guard(mutex, &data->mutex) @@ -262,7 +283,8 @@ static ssize_t edge_show(struct device *dev, struct device_attribute *attr, static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); ssize_t status = size; int flags; @@ -289,7 +311,6 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) @@ -318,7 +339,8 @@ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) static ssize_t active_low_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); struct gpio_desc *desc = data->desc; int value; @@ -332,7 +354,8 @@ static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); ssize_t status; long value; @@ -344,48 +367,34 @@ static ssize_t active_low_store(struct device *dev, return gpio_sysfs_set_active_low(data, value) ?: size; } -static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = kobj_to_dev(kobj); - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; + struct device_attribute *dev_attr = container_of(attr, + struct device_attribute, attr); umode_t mode = attr->mode; - bool show_direction = data->direction_can_change; + struct gpiod_data *data; - if (attr == &dev_attr_direction.attr) { - if (!show_direction) + if (strcmp(attr->name, "direction") == 0) { + data = container_of(dev_attr, struct gpiod_data, dir_attr); + + if (!data->direction_can_change) mode = 0; - } else if (attr == &dev_attr_edge.attr) { - if (gpiod_to_irq(desc) < 0) + } else if (strcmp(attr->name, "edge") == 0) { + data = container_of(dev_attr, struct gpiod_data, edge_attr); + + if (gpiod_to_irq(data->desc) < 0) mode = 0; - if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + + if (!data->direction_can_change && + test_bit(FLAG_IS_OUT, &data->desc->flags)) mode = 0; } return mode; } -static struct attribute *gpio_attrs[] = { - &dev_attr_direction.attr, - &dev_attr_edge.attr, - &dev_attr_value.attr, - &dev_attr_active_low.attr, - NULL, -}; - -static const struct attribute_group gpio_group = { - .attrs = gpio_attrs, - .is_visible = gpio_is_visible, -}; - -static const struct attribute_group *gpio_groups[] = { - &gpio_group, - NULL -}; - /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) @@ -645,6 +654,21 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) return dev_get_drvdata(cdev); }; +static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = 0644; + dev_attr->show = show; + dev_attr->store = store; +} + /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -665,6 +689,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpiod_data *desc_data; struct gpio_device *gdev; + struct attribute **attrs; struct device *dev; int status; @@ -709,8 +734,26 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) else desc_data->direction_can_change = false; + gpiod_attr_init(&desc_data->dir_attr, "direction", + direction_show, direction_store); + gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store); + gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store); + gpiod_attr_init(&desc_data->active_low_attr, "active_low", + active_low_show, active_low_store); + + attrs = desc_data->class_attrs; + desc_data->class_attr_group.is_visible = gpio_is_visible; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION] = &desc_data->dir_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_VALUE] = &desc_data->val_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_EDGE] = &desc_data->edge_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW] = &desc_data->active_low_attr.attr; + + desc_data->class_attr_group.attrs = desc_data->class_attrs; + desc_data->class_attr_groups[0] = &desc_data->class_attr_group; + dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), desc_data, gpio_groups, + MKDEV(0, 0), desc_data, + desc_data->class_attr_groups, "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); -- GitLab From 1cd53df733c21ae0d344a2dec941a3e2a06fefd9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:54 +0200 Subject: [PATCH 750/868] gpio: sysfs: don't look up exported lines as class devices In preparation for adding a parallel, per-chip attribute group for exported GPIO lines, stop using class device APIs to refer to it in the code. When unregistering the chip, don't call class_find_device() but instead store exported lines in a linked list inside the GPIO chip data object and look it up there. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-7-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 60 ++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index e10d720ee0adb..ccc293a4cc5d5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -42,7 +42,10 @@ enum { }; struct gpiod_data { + struct list_head list; + struct gpio_desc *desc; + struct device *dev; struct mutex mutex; struct kernfs_node *value_kn; @@ -62,6 +65,7 @@ struct gpiod_data { }; struct gpiodev_data { + struct list_head exported_lines; struct gpio_device *gdev; struct device *cdev_id; /* Class device by GPIO device ID */ struct device *cdev_base; /* Class device by GPIO base */ @@ -687,10 +691,10 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { + struct gpiodev_data *gdev_data; struct gpiod_data *desc_data; struct gpio_device *gdev; struct attribute **attrs; - struct device *dev; int status; /* can't export until sysfs is available ... */ @@ -751,25 +755,40 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) desc_data->class_attr_group.attrs = desc_data->class_attrs; desc_data->class_attr_groups[0] = &desc_data->class_attr_group; - dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), desc_data, - desc_data->class_attr_groups, - "gpio%u", desc_to_gpio(desc)); - if (IS_ERR(dev)) { - status = PTR_ERR(dev); + /* + * Note: we need to continue passing desc_data here as there's still + * at least one known user of gpiod_export_link() in the tree. This + * function still uses class_find_device() internally. + */ + desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), desc_data, + desc_data->class_attr_groups, + "gpio%u", + desc_to_gpio(desc)); + if (IS_ERR(desc_data->dev)) { + status = PTR_ERR(desc_data->dev); goto err_free_data; } - desc_data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value"); + desc_data->value_kn = sysfs_get_dirent(desc_data->dev->kobj.sd, + "value"); if (!desc_data->value_kn) { status = -ENODEV; goto err_unregister_device; } + gdev_data = gdev_get_data(gdev); + if (!gdev_data) { + status = -ENODEV; + goto err_unregister_device; + } + + list_add(&desc_data->list, &gdev_data->exported_lines); + return 0; err_unregister_device: - device_unregister(dev); + device_unregister(desc_data->dev); err_free_data: kfree(desc_data); err_clear_bit: @@ -828,8 +847,9 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *desc_data; - struct device *dev; + struct gpiod_data *desc_data = NULL; + struct gpiodev_data *gdev_data; + struct gpio_device *gdev; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); @@ -840,14 +860,22 @@ void gpiod_unexport(struct gpio_desc *desc) if (!test_bit(FLAG_EXPORT, &desc->flags)) return; - dev = class_find_device(&gpio_class, NULL, desc, match_export); - if (!dev) + gdev = gpiod_to_gpio_device(desc); + gdev_data = gdev_get_data(gdev); + if (!gdev_data) + return; + + list_for_each_entry(desc_data, &gdev_data->exported_lines, list) + if (gpiod_is_equal(desc, desc_data->desc)) + break; + + if (!desc_data) return; - desc_data = dev_get_drvdata(dev); + list_del(&desc_data->list); clear_bit(FLAG_EXPORT, &desc->flags); sysfs_put(desc_data->value_kn); - device_unregister(dev); + device_unregister(desc_data->dev); /* * Release irq after deregistration to prevent race with @@ -857,7 +885,6 @@ void gpiod_unexport(struct gpio_desc *desc) gpio_sysfs_free_irq(desc_data); } - put_device(dev); mutex_destroy(&desc_data->mutex); kfree(desc_data); } @@ -899,6 +926,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return -ENOMEM; data->gdev = gdev; + INIT_LIST_HEAD(&data->exported_lines); guard(mutex)(&sysfs_lock); -- GitLab From 4fa93223e03eea3243db83786f556b6c1494de3e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:55 +0200 Subject: [PATCH 751/868] gpio: sysfs: export the GPIO directory locally in the gpiochip directory As a way to allow the user-space to stop referring to GPIOs by their global numbers, introduce a parallel group of line attributes for exported GPIO that live inside the GPIO chip class device and are referred to by their HW offset within their parent chip. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-8-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- Documentation/ABI/obsolete/sysfs-gpio | 3 ++ drivers/gpio/gpiolib-sysfs.c | 51 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/obsolete/sysfs-gpio b/Documentation/ABI/obsolete/sysfs-gpio index ff694708a3bef..0d3f12c4dcbde 100644 --- a/Documentation/ABI/obsolete/sysfs-gpio +++ b/Documentation/ABI/obsolete/sysfs-gpio @@ -27,6 +27,9 @@ Description: /base ... (r/o) same as N /label ... (r/o) descriptive chip name /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) + /gpio + /value ... always readable, writes fail for input GPIOs + /direction ... r/w as: in, out (default low); write: high, low /chipX ... for each gpiochip; #X is the gpio device ID /export ... asks the kernel to export a GPIO at HW offset X to userspace /unexport ... to return a GPIO at HW offset X to the kernel diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index ccc293a4cc5d5..563e38456c33c 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -41,6 +41,13 @@ enum { GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, }; +enum { + GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION = 0, + GPIO_SYSFS_LINE_CHIP_ATTR_VALUE, + GPIO_SYSFS_LINE_CHIP_ATTR_SENTINEL, + GPIO_SYSFS_LINE_CHIP_ATTR_SIZE, +}; + struct gpiod_data { struct list_head list; @@ -54,6 +61,7 @@ struct gpiod_data { bool direction_can_change; + struct kobject *parent; struct device_attribute dir_attr; struct device_attribute val_attr; struct device_attribute edge_attr; @@ -62,6 +70,10 @@ struct gpiod_data { struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE]; struct attribute_group class_attr_group; const struct attribute_group *class_attr_groups[2]; + + struct attribute *chip_attrs[GPIO_SYSFS_LINE_CHIP_ATTR_SIZE]; + struct attribute_group chip_attr_group; + const struct attribute_group *chip_attr_groups[2]; }; struct gpiodev_data { @@ -691,6 +703,7 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { + char *path __free(kfree) = NULL; struct gpiodev_data *gdev_data; struct gpiod_data *desc_data; struct gpio_device *gdev; @@ -780,13 +793,46 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) gdev_data = gdev_get_data(gdev); if (!gdev_data) { status = -ENODEV; - goto err_unregister_device; + goto err_put_dirent; + } + + desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u", + gpio_chip_hwgpio(desc)); + if (!desc_data->chip_attr_group.name) { + status = -ENOMEM; + goto err_put_dirent; + } + + attrs = desc_data->chip_attrs; + desc_data->chip_attr_group.is_visible = gpio_is_visible; + attrs[GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION] = &desc_data->dir_attr.attr; + attrs[GPIO_SYSFS_LINE_CHIP_ATTR_VALUE] = &desc_data->val_attr.attr; + + desc_data->chip_attr_group.attrs = attrs; + desc_data->chip_attr_groups[0] = &desc_data->chip_attr_group; + + desc_data->parent = &gdev_data->cdev_id->kobj; + status = sysfs_create_groups(desc_data->parent, + desc_data->chip_attr_groups); + if (status) + goto err_free_name; + + path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc)); + if (!path) { + status = -ENOMEM; + goto err_remove_groups; } list_add(&desc_data->list, &gdev_data->exported_lines); return 0; +err_remove_groups: + sysfs_remove_groups(desc_data->parent, desc_data->chip_attr_groups); +err_free_name: + kfree(desc_data->chip_attr_group.name); +err_put_dirent: + sysfs_put(desc_data->value_kn); err_unregister_device: device_unregister(desc_data->dev); err_free_data: @@ -883,6 +929,9 @@ void gpiod_unexport(struct gpio_desc *desc) */ if (desc_data->irq_flags) gpio_sysfs_free_irq(desc_data); + + sysfs_remove_groups(desc_data->parent, + desc_data->chip_attr_groups); } mutex_destroy(&desc_data->mutex); -- GitLab From e69c6db4cdbc149ff090f1449a114c33ba766dc8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:56 +0200 Subject: [PATCH 752/868] gpio: sysfs: allow disabling the legacy parts of the GPIO sysfs interface Add a Kconfig switch allowing to disable the legacy parts of the GPIO sysfs interface. This means that even though we keep the /sys/class/gpio/ directory, it no longer contains the global export/unexport attribute pair (instead, the user should use the per-chip export/unpexport) nor the gpiochip$BASE entries. This option default to y if GPIO sysfs is enabled but we'll default it to n at some point in the future. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-9-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 8 +++++++ drivers/gpio/gpiolib-sysfs.c | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6802e549621b2..12bdf6e965f19 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -69,6 +69,14 @@ config GPIO_SYSFS use the character device /dev/gpiochipN with the appropriate ioctl() operations instead. +config GPIO_SYSFS_LEGACY + bool "Enable legacy functionalities of the sysfs interface" + depends on GPIO_SYSFS + default y if GPIO_SYSFS + help + Say Y here if you want to enable the legacy, global GPIO + numberspace-based functionalities of the sysfs interface. + config GPIO_CDEV bool "Character device (/dev/gpiochipN) support" if EXPERT default y diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 563e38456c33c..f31adc56bef1e 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -24,6 +24,8 @@ #include "gpiolib.h" #include "gpiolib-sysfs.h" +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) + struct kernfs_node; #define GPIO_IRQF_TRIGGER_NONE 0 @@ -41,6 +43,8 @@ enum { GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, }; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ + enum { GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION = 0, GPIO_SYSFS_LINE_CHIP_ATTR_VALUE, @@ -55,21 +59,26 @@ struct gpiod_data { struct device *dev; struct mutex mutex; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct kernfs_node *value_kn; int irq; unsigned char irq_flags; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ bool direction_can_change; struct kobject *parent; struct device_attribute dir_attr; struct device_attribute val_attr; + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct device_attribute edge_attr; struct device_attribute active_low_attr; struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE]; struct attribute_group class_attr_group; const struct attribute_group *class_attr_groups[2]; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ struct attribute *chip_attrs[GPIO_SYSFS_LINE_CHIP_ATTR_SIZE]; struct attribute_group chip_attr_group; @@ -80,7 +89,9 @@ struct gpiodev_data { struct list_head exported_lines; struct gpio_device *gdev; struct device *cdev_id; /* Class device by GPIO device ID */ +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct device *cdev_base; /* Class device by GPIO base */ +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ }; /* @@ -188,6 +199,7 @@ static ssize_t value_store(struct device *dev, struct device_attribute *attr, return size; } +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { struct gpiod_data *data = priv; @@ -383,6 +395,7 @@ static ssize_t active_low_store(struct device *dev, return gpio_sysfs_set_active_low(data, value) ?: size; } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) @@ -397,6 +410,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, if (!data->direction_can_change) mode = 0; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) } else if (strcmp(attr->name, "edge") == 0) { data = container_of(dev_attr, struct gpiod_data, edge_attr); @@ -406,6 +420,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, if (!data->direction_can_change && test_bit(FLAG_IS_OUT, &data->desc->flags)) mode = 0; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ } return mode; @@ -426,6 +441,7 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, * /ngpio ... matching gpio_chip.ngpio */ +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -434,6 +450,7 @@ static ssize_t base_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%u\n", data->gdev->base); } static DEVICE_ATTR_RO(base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -558,6 +575,7 @@ static struct device_attribute dev_attr_unexport = __ATTR(unexport, 0200, NULL, chip_unexport_store); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, @@ -565,6 +583,7 @@ static struct attribute *gpiochip_attrs[] = { NULL, }; ATTRIBUTE_GROUPS(gpiochip); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static struct attribute *gpiochip_ext_attrs[] = { &dev_attr_label.attr, @@ -575,6 +594,7 @@ static struct attribute *gpiochip_ext_attrs[] = { }; ATTRIBUTE_GROUPS(gpiochip_ext); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) @@ -639,10 +659,13 @@ static struct attribute *gpio_class_attrs[] = { NULL, }; ATTRIBUTE_GROUPS(gpio_class); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ static const struct class gpio_class = { .name = "gpio", +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) .class_groups = gpio_class_groups, +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ }; static int match_gdev(struct device *dev, const void *desc) @@ -754,6 +777,8 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) gpiod_attr_init(&desc_data->dir_attr, "direction", direction_show, direction_store); gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store); + +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store); gpiod_attr_init(&desc_data->active_low_attr, "active_low", active_low_show, active_low_store); @@ -789,6 +814,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) status = -ENODEV; goto err_unregister_device; } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ gdev_data = gdev_get_data(gdev); if (!gdev_data) { @@ -832,10 +858,12 @@ err_remove_groups: err_free_name: kfree(desc_data->chip_attr_group.name); err_put_dirent: +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) sysfs_put(desc_data->value_kn); err_unregister_device: device_unregister(desc_data->dev); err_free_data: +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ kfree(desc_data); err_clear_bit: clear_bit(FLAG_EXPORT, &desc->flags); @@ -844,12 +872,14 @@ err_clear_bit: } EXPORT_SYMBOL_GPL(gpiod_export); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) static int match_export(struct device *dev, const void *desc) { struct gpiod_data *data = dev_get_drvdata(dev); return gpiod_is_equal(data->desc, desc); } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ /** * gpiod_export_link - create a sysfs link to an exported GPIO node @@ -866,6 +896,7 @@ static int match_export(struct device *dev, const void *desc) int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc) { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) struct device *cdev; int ret; @@ -882,6 +913,9 @@ int gpiod_export_link(struct device *dev, const char *name, put_device(cdev); return ret; +#else + return -EOPNOTSUPP; +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ } EXPORT_SYMBOL_GPL(gpiod_export_link); @@ -920,6 +954,7 @@ void gpiod_unexport(struct gpio_desc *desc) list_del(&desc_data->list); clear_bit(FLAG_EXPORT, &desc->flags); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) sysfs_put(desc_data->value_kn); device_unregister(desc_data->dev); @@ -929,6 +964,7 @@ void gpiod_unexport(struct gpio_desc *desc) */ if (desc_data->irq_flags) gpio_sysfs_free_irq(desc_data); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ sysfs_remove_groups(desc_data->parent, desc_data->chip_attr_groups); @@ -979,6 +1015,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) guard(mutex)(&sysfs_lock); +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) /* use chip->base for the ID; it's already known to be unique */ data->cdev_base = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), data, @@ -990,13 +1027,16 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) kfree(data); return err; } +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ data->cdev_id = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), data, gpiochip_ext_groups, "chip%d", gdev->id); if (IS_ERR(data->cdev_id)) { +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ err = PTR_ERR(data->cdev_id); kfree(data); return err; @@ -1016,7 +1056,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) if (!data) return; +#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY) device_unregister(data->cdev_base); +#endif /* CONFIG_GPIO_SYSFS_LEGACY */ device_unregister(data->cdev_id); kfree(data); } -- GitLab From 0c0438d444a7814783099c9028823bff5977e4f0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:57 +0200 Subject: [PATCH 753/868] gpio: TODO: remove the task for the sysfs rework Remove the completed task tracking the rework of the sysfs interface and add a new task to track the removal of the legacy bits and pieces. Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-10-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/TODO | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index ef53892cb44d7..7a09a4f58551b 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -188,16 +188,12 @@ remove the old ones and finally rename the new ones back to the old names. ------------------------------------------------------------------------------- -Extend the sysfs ABI to allow exporting lines by their HW offsets +Remove legacy sysfs features -The need to support the sysfs GPIO class is one of the main obstacles to -removing the global GPIO numberspace from the kernel. In order to wean users -off using global numbers from user-space, extend the existing interface with -new per-gpiochip export/unexport attributes that allow to refer to GPIOs using -their hardware offsets within the chip. - -Encourage users to switch to using them and eventually remove the existing -global export/unexport attribues. +We have two parallel per-chip class devices and per-exported-line attribute +groups in sysfs. One is using the obsolete global GPIO numberspace and the +second relies on hardware offsets of pins within the chip. Remove the former +once user-space has switched to using the latter. ------------------------------------------------------------------------------- -- GitLab From 5103fbb7b59f7a078284a345d82bdab0f0ee6d08 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Jul 2025 17:58:34 -0500 Subject: [PATCH 754/868] gpio: viperboard: Unlock on error in vprbrd_gpiob_direction_output() Unlock before returning if vprbrd_gpiob_setdir() fails. Fixes: 55e2d1eec110 ("gpio: viperboard: use new GPIO line value setter callbacks") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/9e72018c-e46e-4e55-83e4-503da4d022fc@sabinyo.mountain Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-viperboard.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index 3eba77f981d3a..e8e906b54d518 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -378,15 +378,13 @@ static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, gpio->gpiob_out |= (1 << offset); mutex_lock(&vb->lock); - ret = vprbrd_gpiob_setdir(vb, offset, 1); + mutex_unlock(&vb->lock); if (ret) { dev_err(chip->parent, "usb error setting pin to output\n"); return ret; } - mutex_unlock(&vb->lock); - return vprbrd_gpiob_set(chip, offset, value); } -- GitLab From 27cb8f702eb789f97f7a8bd5a91d76c65a937b2f Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Mon, 14 Jul 2025 14:45:42 +0800 Subject: [PATCH 755/868] gpio: loongson-64bit: Extend GPIO irq support Add the interrupt enable register offset (inten_offset) so that GPIO interrupts can be enabled normally on more models. According to the latest interface specifications, the definition of GPIO interrupts in ACPI is similar to that in FDT. The GPIO interrupts are listed one by one according to the GPIO number, and the corresponding interrupt number can be obtained directly through the GPIO number specified by the consumer. Signed-off-by: Xi Ruoyao Signed-off-by: Binbin Zhou Reviewed-by: Huacai Chen Link: https://lore.kernel.org/r/20250714064542.2276247-1-zhoubinbin@loongson.cn [Bartosz: tweaked the commit message] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-loongson-64bit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index 26227669f0269..d5b2ad848519c 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -222,6 +222,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { @@ -230,6 +231,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = { .conf_offset = 0x0, .in_offset = 0x20, .out_offset = 0x10, + .inten_offset = 0x30, }; static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = { @@ -246,6 +248,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { @@ -254,6 +257,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = { .conf_offset = 0x800, .in_offset = 0xa00, .out_offset = 0x900, + .inten_offset = 0xb00, }; /* LS7A2000 chipset GPIO */ @@ -263,6 +267,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls7a2000_data0 = { .conf_offset = 0x800, .in_offset = 0xa00, .out_offset = 0x900, + .inten_offset = 0xb00, }; /* LS7A2000 ACPI GPIO */ @@ -281,6 +286,7 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls3a6000_data = { .conf_offset = 0x0, .in_offset = 0xc, .out_offset = 0x8, + .inten_offset = 0x14, }; static const struct of_device_id loongson_gpio_of_match[] = { -- GitLab From aa84580e058c4d7567b2a5e5a9d12f94af75d297 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 16 Jul 2025 09:49:58 +0200 Subject: [PATCH 756/868] ASoC: dt-bindings: qcom,lpass-va-macro: Define clock-names in top-level Device variants use different amount of clock inputs, but all of them are in the same order, 'clock-names' in top-level properties can define the list and each if:then: block can only narrow the number of items. This is preferred syntax, because it keeps list unified among devices and encourages adding new entries to the end of the list, instead of adding them in the middle. The change has no functional impact, but partially reverts approach implemented in commit cfad817095e1 ("ASoC: dt-bindings: qcom,lpass-va-macro: Add missing NPL clock"). Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250716074957.102402-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- .../bindings/sound/qcom,lpass-va-macro.yaml | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index f41deaa6f4df5..dd549db6c841b 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -40,7 +40,11 @@ properties: clock-names: minItems: 1 - maxItems: 4 + items: + - const: mclk + - const: macro + - const: dcodec + - const: npl clock-output-names: maxItems: 1 @@ -80,8 +84,7 @@ allOf: clocks: maxItems: 1 clock-names: - items: - - const: mclk + maxItems: 1 - if: properties: @@ -94,10 +97,8 @@ allOf: minItems: 3 maxItems: 3 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec + minItems: 3 + maxItems: 3 - if: properties: @@ -112,11 +113,8 @@ allOf: minItems: 4 maxItems: 4 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec - - const: npl + minItems: 4 + maxItems: 4 - if: properties: @@ -130,10 +128,8 @@ allOf: minItems: 3 maxItems: 3 clock-names: - items: - - const: mclk - - const: macro - - const: dcodec + minItems: 3 + maxItems: 3 unevaluatedProperties: false -- GitLab From 8778837f0a5b7c1bc5dbf0cccd7619fec6981588 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 16 Jul 2025 08:57:07 +0200 Subject: [PATCH 757/868] ASoC: codec: tlv320aic32x4: Fix reset GPIO check rstn_gpio being a GPIO descriptor the check is wrong (inverted) for releasing the reset of the codec. Fixes: 790d5f8ee6f2 ("ASoC: codec: tlv320aic32x4: Convert to GPIO descriptors") Signed-off-by: Alexander Stein Reviewed-by: Peng Fan Link: https://patch.msgid.link/20250716065708.4041153-1-alexander.stein@ew.tq-group.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 3b89980e9bcf2..7399080f8580c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1388,7 +1388,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap, return ret; } - if (!aic32x4->rstn_gpio) { + if (aic32x4->rstn_gpio) { ndelay(10); /* deassert reset */ gpiod_set_value_cansleep(aic32x4->rstn_gpio, 0); -- GitLab From 63be976da994260ea116c431a2e61485dbede1b0 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Wed, 16 Jul 2025 10:08:30 +0800 Subject: [PATCH 758/868] regulator: rt6160: Add rt6166 vout min_uV setting for compatible 1. remove unintentional GPL change 2. using switch case for Device ID probe check. Signed-off-by: Jeff Chang Link: https://patch.msgid.link/20250716021230.2660564-1-jeff_chang@richtek.com Signed-off-by: Mark Brown --- drivers/regulator/rt6160-regulator.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/rt6160-regulator.c b/drivers/regulator/rt6160-regulator.c index e2a0eee95c617..548ffdf537d30 100644 --- a/drivers/regulator/rt6160-regulator.c +++ b/drivers/regulator/rt6160-regulator.c @@ -31,8 +31,11 @@ #define RT6160_PGSTAT_MASK BIT(0) #define RT6160_VENDOR_ID 0xA0 +#define RT6166_VENDOR_ID 0xB0 #define RT6160_VOUT_MINUV 2025000 #define RT6160_VOUT_MAXUV 5200000 +#define RT6166_VOUT_MINUV 1800000 +#define RT6166_VOUD_MAXUV 4950000 #define RT6160_VOUT_STPUV 25000 #define RT6160_N_VOUTS ((RT6160_VOUT_MAXUV - RT6160_VOUT_MINUV) / RT6160_VOUT_STPUV + 1) @@ -43,6 +46,7 @@ struct rt6160_priv { struct gpio_desc *enable_gpio; struct regmap *regmap; bool enable_state; + uint8_t devid; }; static const unsigned int rt6160_ramp_tables[] = { @@ -260,15 +264,26 @@ static int rt6160_probe(struct i2c_client *i2c) if (ret) return ret; - if ((devid & RT6160_VID_MASK) != RT6160_VENDOR_ID) { + devid = devid & RT6160_VID_MASK; + + switch (devid) { + case RT6166_VENDOR_ID: + case RT6160_VENDOR_ID: + break; + default: dev_err(&i2c->dev, "VID not correct [0x%02x]\n", devid); return -ENODEV; } + priv->devid = devid; + priv->desc.name = "rt6160-buckboost"; priv->desc.type = REGULATOR_VOLTAGE; priv->desc.owner = THIS_MODULE; - priv->desc.min_uV = RT6160_VOUT_MINUV; + if (priv->devid == RT6166_VENDOR_ID) + priv->desc.min_uV = RT6166_VOUT_MINUV; + else + priv->desc.min_uV = RT6160_VOUT_MINUV; priv->desc.uV_step = RT6160_VOUT_STPUV; if (vsel_active_low) priv->desc.vsel_reg = RT6160_REG_VSELL; -- GitLab From d929cc75e9791def049a90998aaab8934196131c Mon Sep 17 00:00:00 2001 From: Darshan Rathod Date: Wed, 16 Jul 2025 09:59:05 +0000 Subject: [PATCH 759/868] spi: gpio: Use explicit 'unsigned int' for parameter types The C standard allows 'unsigned' as a shorthand for 'unsigned int'. For improved code clarity and consistency with the prevailing kernel coding style, replace the shorthand with the more explicit 'unsigned int' type for function parameters. This is a purely stylistic cleanup and has no functional impact on the generated code. Signed-off-by: Darshan Rathod Link: https://patch.msgid.link/20250716095906.21812-1-darshanrathod475@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index ea5f1b10b79e5..c8dadb532c406 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -104,7 +104,7 @@ static inline int getmiso(const struct spi_device *spi) */ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits); @@ -113,7 +113,7 @@ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits); @@ -122,7 +122,7 @@ static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits); @@ -131,7 +131,7 @@ static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, } static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { if (unlikely(spi->mode & SPI_LSB_FIRST)) return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits); @@ -150,7 +150,7 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, */ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -160,7 +160,7 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -170,7 +170,7 @@ static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) @@ -180,7 +180,7 @@ static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, } static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits, unsigned flags) + unsigned int nsecs, u32 word, u8 bits, unsigned int flags) { flags = spi->controller->flags; if (unlikely(spi->mode & SPI_LSB_FIRST)) -- GitLab From 951a6d8d41289b86a564ee5563ededa702b62b1b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Jul 2025 18:01:39 -0500 Subject: [PATCH 760/868] spi: stm32-ospi: Fix NULL vs IS_ERR() bug in stm32_ospi_get_resources() This code was changed from using devm_ioremap() which returns NULL to using devm_ioremap_resource() which returns error pointers. Update the error checking to match. Fixes: defe01abfb7f ("spi: stm32-ospi: Use of_reserved_mem_region_to_resource() for "memory-region"") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/fb2a26a2-119b-4b5a-8d44-b29e2c736081@sabinyo.mountain Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 72baa402a2c3a..f36fd36da2692 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -823,9 +823,9 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) if (!ret) { ospi->mm_size = resource_size(res); ospi->mm_base = devm_ioremap_resource(dev, res); - if (!ospi->mm_base) { + if (IS_ERR(ospi->mm_base)) { dev_err(dev, "unable to map memory region: %pR\n", res); - ret = -ENOMEM; + ret = PTR_ERR(ospi->mm_base); goto err_dma; } -- GitLab From 03aa2ed9e187e42f25b3871b691d535fc19156c4 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Wed, 16 Jul 2025 16:23:00 +0800 Subject: [PATCH 761/868] ASoC: Intel: sof_rt5682: Add HDMI-In capture with rt5682 support for PTL. Added match table entry on ptl machines to support HDMI-In capture with rt5682 I2S audio codec. also added the respective quirk configuration in rt5682 machine driver. Signed-off-by: Balamurugan C Signed-off-by: Bard Liao Link: https://patch.msgid.link/20250716082300.1810352-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 7 +++++++ sound/soc/intel/common/soc-acpi-intel-ptl-match.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index f5925bd0a3fc6..4994aaccc583a 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -892,6 +892,13 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_PORT_BT_OFFLOAD(2) | SOF_BT_OFFLOAD_PRESENT), }, + { + .name = "ptl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_SSP_PORT_CODEC(1) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_SSP_MASK_HDMI_CAPTURE(0x5)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index a8fee8cf49136..e292701dfcfe5 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -32,6 +32,13 @@ static const struct snd_soc_acpi_codecs ptl_lt6911_hdmi = { }; struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = { + { + .comp_ids = &ptl_rt5682_rt5682s_hp, + .drv_name = "ptl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &ptl_lt6911_hdmi, + .sof_tplg_filename = "sof-ptl-rt5682-ssp1-hdmi-ssp02.tplg", + }, { .comp_ids = &ptl_rt5682_rt5682s_hp, .drv_name = "ptl_rt5682_def", -- GitLab From 3ee9f060826e8262b2c40fec5944994562d4aa10 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jul 2025 15:20:21 +0200 Subject: [PATCH 762/868] ACPI: APEI: MAINTAINERS: Update reviewers for APEI Update the ACPI APEI entry in MAINTAINERS by dropping the reviewers who have not been responsive for over a year and adding a list of new reviewers. Signed-off-by: Rafael J. Wysocki Acked-by: Hanjun Guo Acked-by: Mauro Carvalho Chehab Acked-by: Shuai Xue Link: https://patch.msgid.link/12722151.O9o76ZdvQC@rjwysocki.net Signed-off-by: Rafael J. Wysocki --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0c1d245bf7b84..e9f49c6f92448 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -306,10 +306,11 @@ F: tools/power/acpi/ ACPI APEI M: "Rafael J. Wysocki" -R: Len Brown -R: James Morse R: Tony Luck R: Borislav Petkov +R: Hanjun Guo +R: Mauro Carvalho Chehab +R: Shuai Xue L: linux-acpi@vger.kernel.org F: drivers/acpi/apei/ -- GitLab From 79a5ae3c4c5eb7e38e0ebe4d6bf602d296080060 Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Mon, 14 Jul 2025 19:42:11 +0800 Subject: [PATCH 763/868] ACPI: APEI: send SIGBUS to current task if synchronous memory error not recovered If a synchronous error is detected as a result of user-space process triggering a 2-bit uncorrected error, the CPU will take a synchronous error exception such as Synchronous External Abort (SEA) on Arm64. The kernel will queue a memory_failure() work which poisons the related page, unmaps the page, and then sends a SIGBUS to the process, so that a system wide panic can be avoided. However, no memory_failure() work will be queued when abnormal synchronous errors occur. These errors can include situations like invalid PA, unexpected severity, no memory failure config support, invalid GUID section, etc. In such a case, the user-space process will trigger SEA again. This loop can potentially exceed the platform firmware threshold or even trigger a kernel hard lockup, leading to a system reboot. Fix it by performing a force kill if no memory_failure() work is queued for synchronous errors. Signed-off-by: Shuai Xue Reviewed-by: Jarkko Sakkinen Reviewed-by: Jonathan Cameron Reviewed-by: Yazen Ghannam Reviewed-by: Jane Chu Reviewed-by: Hanjun Guo Link: https://patch.msgid.link/20250714114212.31660-2-xueshuai@linux.alibaba.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3d44f926afe8e..bda33a0f0a013 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -902,6 +902,17 @@ static bool ghes_do_proc(struct ghes *ghes, } } + /* + * If no memory failure work is queued for abnormal synchronous + * errors, do a force kill. + */ + if (sync && !queued) { + dev_err(ghes->dev, + HW_ERR GHES_PFX "%s:%d: synchronous unrecoverable error (SIGBUS)\n", + current->comm, task_pid_nr(current)); + force_sig(SIGBUS); + } + return queued; } -- GitLab From c1f1fda141373d7253b4c1497043b0ef85f534ce Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Mon, 14 Jul 2025 19:42:12 +0800 Subject: [PATCH 764/868] ACPI: APEI: handle synchronous exceptions in task work The memory uncorrected error could be signaled by asynchronous interrupt (specifically, SPI in arm64 platform), e.g. when an error is detected by a background scrubber, or signaled by synchronous exception (specifically, data abort exception in arm64 platform), e.g. when a CPU tries to access a poisoned cache line. Currently, both synchronous and asynchronous errors use memory_failure_queue() to schedule memory_failure() to exectute in a kworker context. As a result, when a user-space process is accessing a poisoned data, a data abort is taken and the memory_failure() is executed in the kworker context, which: - will send wrong si_code by SIGBUS signal in early_kill mode, and - can not kill the user-space in some cases resulting a synchronous error infinite loop Issue 1: send wrong si_code in early_kill mode Since commit a70297d22132 ("ACPI: APEI: set memory failure flags as MF_ACTION_REQUIRED on synchronous events")', the flag MF_ACTION_REQUIRED could be used to determine whether a synchronous exception occurs on ARM64 platform. When a synchronous exception is detected, the kernel is expected to terminate the current process which has accessed a poisoned page. This is done by sending a SIGBUS signal with error code BUS_MCEERR_AR, indicating an action-required machine check error on read. However, when kill_proc() is called to terminate the processes who has the poisoned page mapped, it sends the incorrect SIGBUS error code BUS_MCEERR_AO because the context in which it operates is not the one where the error was triggered. To reproduce this problem: #sysctl -w vm.memory_failure_early_kill=1 vm.memory_failure_early_kill = 1 # STEP2: inject an UCE error and consume it to trigger a synchronous error #einj_mem_uc single 0: single vaddr = 0xffffb0d75400 paddr = 4092d55b400 injecting ... triggering ... signal 7 code 5 addr 0xffffb0d75000 page not present Test passed The si_code (code 5) from einj_mem_uc indicates that it is BUS_MCEERR_AO error and it is not factually correct. After this change: # STEP1: enable early kill mode #sysctl -w vm.memory_failure_early_kill=1 vm.memory_failure_early_kill = 1 # STEP2: inject an UCE error and consume it to trigger a synchronous error #einj_mem_uc single 0: single vaddr = 0xffffb0d75400 paddr = 4092d55b400 injecting ... triggering ... signal 7 code 4 addr 0xffffb0d75000 page not present Test passed The si_code (code 4) from einj_mem_uc indicates that it is a BUS_MCEERR_AR error as expected. Issue 2: a synchronous error infinite loop If a user-space process, e.g. devmem, accesses a poisoned page for which the HWPoison flag is set, kill_accessing_process() is called to send SIGBUS to current processs with error info. Since the memory_failure() is executed in the kworker context, it will just do nothing but return EFAULT. So, devmem will access the posioned page and trigger an exception again, resulting in a synchronous error infinite loop. Such exception loop may cause platform firmware to exceed some threshold and reboot when Linux could have recovered from this error. To reproduce this problem: # STEP 1: inject an UCE error, and kernel will set HWPosion flag for related page #einj_mem_uc single 0: single vaddr = 0xffffb0d75400 paddr = 4092d55b400 injecting ... triggering ... signal 7 code 4 addr 0xffffb0d75000 page not present Test passed # STEP 2: access the same page and it will trigger a synchronous error infinite loop devmem 0x4092d55b400 To fix above two issues, queue memory_failure() as a task_work so that it runs in the context of the process that is actually consuming the poisoned data. Signed-off-by: Shuai Xue Tested-by: Ma Wupeng Reviewed-by: Kefeng Wang Reviewed-by: Xiaofei Tan Reviewed-by: Baolin Wang Reviewed-by: Jarkko Sakkinen Reviewed-by: Jonathan Cameron Reviewed-by: Jane Chu Reviewed-by: Yazen Ghannam Reviewed-by: Hanjun Guo Link: https://patch.msgid.link/20250714114212.31660-3-xueshuai@linux.alibaba.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 79 +++++++++++++++++++++++----------------- include/acpi/ghes.h | 3 -- include/linux/mm.h | 1 - mm/memory-failure.c | 13 ------- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index bda33a0f0a013..a0d54993edb3b 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -464,28 +464,41 @@ static void ghes_clear_estatus(struct ghes *ghes, ghes_ack_error(ghes->generic_v2); } -/* - * Called as task_work before returning to user-space. - * Ensure any queued work has been done before we return to the context that - * triggered the notification. +/** + * struct ghes_task_work - for synchronous RAS event + * + * @twork: callback_head for task work + * @pfn: page frame number of corrupted page + * @flags: work control flags + * + * Structure to pass task work to be handled before + * returning to user-space via task_work_add(). */ -static void ghes_kick_task_work(struct callback_head *head) +struct ghes_task_work { + struct callback_head twork; + u64 pfn; + int flags; +}; + +static void memory_failure_cb(struct callback_head *twork) { - struct acpi_hest_generic_status *estatus; - struct ghes_estatus_node *estatus_node; - u32 node_len; + struct ghes_task_work *twcb = container_of(twork, struct ghes_task_work, twork); + int ret; - estatus_node = container_of(head, struct ghes_estatus_node, task_work); - if (IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) - memory_failure_queue_kick(estatus_node->task_work_cpu); + ret = memory_failure(twcb->pfn, twcb->flags); + gen_pool_free(ghes_estatus_pool, (unsigned long)twcb, sizeof(*twcb)); - estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - node_len = GHES_ESTATUS_NODE_LEN(cper_estatus_len(estatus)); - gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, node_len); + if (!ret || ret == -EHWPOISON || ret == -EOPNOTSUPP) + return; + + pr_err("%#llx: Sending SIGBUS to %s:%d due to hardware memory corruption\n", + twcb->pfn, current->comm, task_pid_nr(current)); + force_sig(SIGBUS); } static bool ghes_do_memory_failure(u64 physical_addr, int flags) { + struct ghes_task_work *twcb; unsigned long pfn; if (!IS_ENABLED(CONFIG_ACPI_APEI_MEMORY_FAILURE)) @@ -499,6 +512,18 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags) return false; } + if (flags == MF_ACTION_REQUIRED && current->mm) { + twcb = (void *)gen_pool_alloc(ghes_estatus_pool, sizeof(*twcb)); + if (!twcb) + return false; + + twcb->pfn = pfn; + twcb->flags = flags; + init_task_work(&twcb->twork, memory_failure_cb); + task_work_add(current, &twcb->twork, TWA_RESUME); + return true; + } + memory_failure_queue(pfn, flags); return true; } @@ -842,7 +867,7 @@ int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd) } EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, "CXL"); -static bool ghes_do_proc(struct ghes *ghes, +static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { int sev, sec_sev; @@ -912,8 +937,6 @@ static bool ghes_do_proc(struct ghes *ghes, current->comm, task_pid_nr(current)); force_sig(SIGBUS); } - - return queued; } static void __ghes_print_estatus(const char *pfx, @@ -1219,9 +1242,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; struct acpi_hest_generic_status *estatus; - bool task_work_pending; u32 len, node_len; - int ret; llnode = llist_del_all(&ghes_estatus_llist); /* @@ -1236,25 +1257,16 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) estatus = GHES_ESTATUS_FROM_NODE(estatus_node); len = cper_estatus_len(estatus); node_len = GHES_ESTATUS_NODE_LEN(len); - task_work_pending = ghes_do_proc(estatus_node->ghes, estatus); + + ghes_do_proc(estatus_node->ghes, estatus); + if (!ghes_estatus_cached(estatus)) { generic = estatus_node->generic; if (ghes_print_estatus(NULL, generic, estatus)) ghes_estatus_cache_add(generic, estatus); } - - if (task_work_pending && current->mm) { - estatus_node->task_work.func = ghes_kick_task_work; - estatus_node->task_work_cpu = smp_processor_id(); - ret = task_work_add(current, &estatus_node->task_work, - TWA_RESUME); - if (ret) - estatus_node->task_work.func = NULL; - } - - if (!estatus_node->task_work.func) - gen_pool_free(ghes_estatus_pool, - (unsigned long)estatus_node, node_len); + gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, + node_len); llnode = next; } @@ -1315,7 +1327,6 @@ static int ghes_in_nmi_queue_one_entry(struct ghes *ghes, estatus_node->ghes = ghes; estatus_node->generic = ghes->generic; - estatus_node->task_work.func = NULL; estatus = GHES_ESTATUS_FROM_NODE(estatus_node); if (__ghes_read_estatus(estatus, buf_paddr, fixmap_idx, len)) { diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index be1dd4c1a9174..ebd21b05fe6ed 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -35,9 +35,6 @@ struct ghes_estatus_node { struct llist_node llnode; struct acpi_hest_generic *generic; struct ghes *ghes; - - int task_work_cpu; - struct callback_head task_work; }; struct ghes_estatus_cache { diff --git a/include/linux/mm.h b/include/linux/mm.h index 0ef2ba0c667af..02dffff2508bc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3896,7 +3896,6 @@ enum mf_flags { int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index, unsigned long count, int mf_flags); extern int memory_failure(unsigned long pfn, int flags); -extern void memory_failure_queue_kick(int cpu); extern int unpoison_memory(unsigned long pfn); extern atomic_long_t num_poisoned_pages __read_mostly; extern int soft_offline_page(unsigned long pfn, int flags); diff --git a/mm/memory-failure.c b/mm/memory-failure.c index b91a33fb6c694..5278132a2b6d9 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -2503,19 +2503,6 @@ static void memory_failure_work_func(struct work_struct *work) } } -/* - * Process memory_failure work queued on the specified CPU. - * Used to avoid return-to-userspace racing with the memory_failure workqueue. - */ -void memory_failure_queue_kick(int cpu) -{ - struct memory_failure_cpu *mf_cpu; - - mf_cpu = &per_cpu(memory_failure_cpu, cpu); - cancel_work_sync(&mf_cpu->work); - memory_failure_work_func(&mf_cpu->work); -} - static int __init memory_failure_init(void) { struct memory_failure_cpu *mf_cpu; -- GitLab From cf115ebad30f08c96f59a39e6a96ef26a146d900 Mon Sep 17 00:00:00 2001 From: Sukrut Heroorkar Date: Wed, 16 Jul 2025 14:35:43 +0200 Subject: [PATCH 765/868] ACPI: TAD: Replace sprintf() with sysfs_emit() Replace sprintf() in *_show() callbacks of sysfs attributes with sysfs_emit(). While the current implementation works, sysfs_emit() helps to prevent potential buffer overflows and aligns with kernel documentation Documentation/filesystems/sysfs.rst. Tested on an x86_64 system with acpi_tad built as a module: - Inserted patched acpi_tad.ko successfully - Verified /sys/devices/platform/ACPI000E:00/time and /caps are accessible - Confirmed correct output from 'cat' with no dmesg errors Signed-off-by: Sukrut Heroorkar Link: https://patch.msgid.link/20250716123543.495628-1-hsukrut3@gmail.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_tad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 825c2a8acea43..91d7d90c47dac 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -233,7 +233,7 @@ static ssize_t time_show(struct device *dev, struct device_attribute *attr, if (ret) return ret; - return sprintf(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", + return sysfs_emit(buf, "%u:%u:%u:%u:%u:%u:%d:%u\n", rt.year, rt.month, rt.day, rt.hour, rt.minute, rt.second, rt.tz, rt.daylight); } @@ -428,7 +428,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, { struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); - return sprintf(buf, "0x%02X\n", dd->capabilities); + return sysfs_emit(buf, "0x%02X\n", dd->capabilities); } static DEVICE_ATTR_RO(caps); -- GitLab From 9a9f71b2a3a7491c10ceea699e1999298db5c596 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 10 Jun 2025 14:32:37 +0200 Subject: [PATCH 766/868] thermal/drivers/rockchip: Rename rk_tsadcv3_tshut_mode The "v" version specifier here refers to the hardware IP revision. Mainline deviated from downstream here by calling the v4 revision v3 as it didn't support the v3 hardware revision at all. This creates needless confusion, so rename it to rk_tsadcv4_tshut_mode to be consistent with what the hardware wants to be called. Signed-off-by: Nicolas Frattaroli Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-1-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano --- drivers/thermal/rockchip_thermal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index a8ad85feb68fb..40c7d234c3ef9 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -1045,7 +1045,7 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs, writel_relaxed(val, regs + TSADCV2_INT_EN); } -static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs, +static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs, enum tshut_mode mode) { u32 val_gpio, val_cru; @@ -1297,7 +1297,7 @@ static const struct rockchip_tsadc_chip rk3588_tsadc_data = { .get_temp = rk_tsadcv4_get_temp, .set_alarm_temp = rk_tsadcv3_alarm_temp, .set_tshut_temp = rk_tsadcv3_tshut_temp, - .set_tshut_mode = rk_tsadcv3_tshut_mode, + .set_tshut_mode = rk_tsadcv4_tshut_mode, .table = { .id = rk3588_code_table, .length = ARRAY_SIZE(rk3588_code_table), -- GitLab From 83f2ef0f1b57445ddf38e18d2c8ffd7f270d56bd Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 10 Jun 2025 14:32:38 +0200 Subject: [PATCH 767/868] dt-bindings: rockchip-thermal: Add RK3576 compatible Add a new compatible for the thermal sensor device on the RK3576 SoC. Acked-by: Rob Herring (Arm) Signed-off-by: Nicolas Frattaroli Acked-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-2-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano --- Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml index b717ea8261ca2..49ceed68c92ce 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml @@ -21,6 +21,7 @@ properties: - rockchip,rk3368-tsadc - rockchip,rk3399-tsadc - rockchip,rk3568-tsadc + - rockchip,rk3576-tsadc - rockchip,rk3588-tsadc - rockchip,rv1108-tsadc -- GitLab From feb69bccf5d3eb31918df86638abc82594390ba5 Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Tue, 10 Jun 2025 14:32:39 +0200 Subject: [PATCH 768/868] thermal/drivers/rockchip: Support RK3576 SoC in the thermal driver The RK3576 SoC has six TS-ADC channels: TOP, BIG_CORE, LITTLE_CORE, DDR, NPU and GPU. Signed-off-by: Ye Zhang [ported to mainline, reworded commit message] Signed-off-by: Nicolas Frattaroli Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-3-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano --- drivers/thermal/rockchip_thermal.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 40c7d234c3ef9..89e3180667e2a 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -1284,6 +1284,28 @@ static const struct rockchip_tsadc_chip rk3568_tsadc_data = { }, }; +static const struct rockchip_tsadc_chip rk3576_tsadc_data = { + /* top, big_core, little_core, ddr, npu, gpu */ + .chn_offset = 0, + .chn_num = 6, /* six channels for tsadc */ + .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */ + .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */ + .tshut_temp = 95000, + .initialize = rk_tsadcv8_initialize, + .irq_ack = rk_tsadcv4_irq_ack, + .control = rk_tsadcv4_control, + .get_temp = rk_tsadcv4_get_temp, + .set_alarm_temp = rk_tsadcv3_alarm_temp, + .set_tshut_temp = rk_tsadcv3_tshut_temp, + .set_tshut_mode = rk_tsadcv4_tshut_mode, + .table = { + .id = rk3588_code_table, + .length = ARRAY_SIZE(rk3588_code_table), + .data_mask = TSADCV4_DATA_MASK, + .mode = ADC_INCREMENT, + }, +}; + static const struct rockchip_tsadc_chip rk3588_tsadc_data = { /* top, big_core0, big_core1, little_core, center, gpu, npu */ .chn_offset = 0, @@ -1342,6 +1364,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = { .compatible = "rockchip,rk3568-tsadc", .data = (void *)&rk3568_tsadc_data, }, + { + .compatible = "rockchip,rk3576-tsadc", + .data = (void *)&rk3576_tsadc_data, + }, { .compatible = "rockchip,rk3588-tsadc", .data = (void *)&rk3588_tsadc_data, -- GitLab From 75b98a2c35319d0e8827576030e110a9c046460f Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 10 Jun 2025 14:32:40 +0200 Subject: [PATCH 769/868] dt-bindings: thermal: rockchip: document otp thermal trim Several Rockchip SoCs, such as the RK3576, can store calibration trim data for thermal sensors in OTP cells. This capability should be documented. Such a rockchip thermal sensor may reference cell handles that store both a chip-wide trim for all the sensors, as well as cell handles for each individual sensor channel pointing to that specific sensor's trim value. Additionally, the thermal sensor may optionally reference cells which store the base in terms of degrees celsius and decicelsius that the trim is relative to. Each SoC that implements this appears to have a slightly different combination of chip-wide trim, base, base fractional part and per-channel trim, so which ones do which is documented in the bindings. Reviewed-by: Rob Herring (Arm) Signed-off-by: Nicolas Frattaroli Acked-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-4-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano --- .../bindings/thermal/rockchip-thermal.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml index 49ceed68c92ce..573f447cc26ed 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.yaml @@ -40,6 +40,17 @@ properties: - const: tsadc - const: apb_pclk + nvmem-cells: + items: + - description: cell handle to where the trim's base temperature is stored + - description: + cell handle to where the trim's tenths of Celsius base value is stored + + nvmem-cell-names: + items: + - const: trim_base + - const: trim_base_frac + resets: minItems: 1 maxItems: 3 @@ -51,6 +62,12 @@ properties: - const: tsadc - const: tsadc-phy + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + "#thermal-sensor-cells": const: 1 @@ -72,6 +89,27 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] +patternProperties: + "@[0-9a-f]+$": + type: object + properties: + reg: + maxItems: 1 + description: sensor ID, a.k.a. channel number + + nvmem-cells: + items: + - description: handle of cell containing calibration data + + nvmem-cell-names: + items: + - const: trim + + required: + - reg + + unevaluatedProperties: false + required: - compatible - reg @@ -80,6 +118,29 @@ required: - clock-names - resets +allOf: + - if: + not: + properties: + compatible: + contains: + const: rockchip,rk3568-tsadc + then: + properties: + nvmem-cells: false + nvmem-cell-names: false + - if: + not: + properties: + compatible: + contains: + enum: + - rockchip,rk3568-tsadc + - rockchip,rk3576-tsadc + then: + patternProperties: + "@[0-9a-f]+$": false + unevaluatedProperties: false examples: -- GitLab From ae332ec0009d762982540635411caefeafa92a5b Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Tue, 10 Jun 2025 14:32:41 +0200 Subject: [PATCH 770/868] thermal/drivers/rockchip: Support reading trim values from OTP Many of the Rockchip SoCs support storing trim values for the sensors in factory programmable memory. These values specify a fixed offset from the sensor's returned temperature to get a more accurate picture of what temperature the silicon is actually at. The way this is implemented is with various OTP cells, which may be absent. There may both be whole-TSADC trim values, as well as per-sensor trim values. In the downstream driver, whole-chip trim values override the per-sensor trim values. This rewrite of the functionality changes the semantics to something I see as slightly more useful: allow the whole-chip trim values to serve as a fallback for lacking per-sensor trim values, instead of overriding already present sensor trim values. Additionally, the chip may specify an offset (trim_base, trim_base_frac) in degrees celsius and degrees decicelsius respectively which defines what the basis is from which the trim, if any, should be calculated from. By default, this is 30 degrees Celsius, but the chip can once again specify a different value through OTP cells. The implementation of these trim calculations have been tested extensively on an RK3576, where it was confirmed to get rid of pesky 1.8 degree Celsius offsets between certain sensors. Signed-off-by: Nicolas Frattaroli Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-5-b6e9efbf1015@collabora.com Signed-off-by: Daniel Lezcano --- drivers/thermal/rockchip_thermal.c | 221 ++++++++++++++++++++++++++--- 1 file changed, 202 insertions(+), 19 deletions(-) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 89e3180667e2a..3beff9b6fac3a 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -69,16 +70,18 @@ struct chip_tsadc_table { * struct rockchip_tsadc_chip - hold the private data of tsadc chip * @chn_offset: the channel offset of the first channel * @chn_num: the channel number of tsadc chip - * @tshut_temp: the hardware-controlled shutdown temperature value + * @trim_slope: used to convert the trim code to a temperature in millicelsius + * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) * @initialize: SoC special initialize tsadc controller method * @irq_ack: clear the interrupt * @control: enable/disable method for the tsadc controller - * @get_temp: get the temperature + * @get_temp: get the raw temperature, unadjusted by trim * @set_alarm_temp: set the high temperature interrupt * @set_tshut_temp: set the hardware-controlled shutdown temperature * @set_tshut_mode: set the hardware-controlled shutdown mode + * @get_trim_code: convert a hardware temperature code to one adjusted for by trim * @table: the chip-specific conversion table */ struct rockchip_tsadc_chip { @@ -86,6 +89,9 @@ struct rockchip_tsadc_chip { int chn_offset; int chn_num; + /* Used to convert trim code to trim temp */ + int trim_slope; + /* The hardware-controlled tshut property */ int tshut_temp; enum tshut_mode tshut_mode; @@ -105,6 +111,8 @@ struct rockchip_tsadc_chip { int (*set_tshut_temp)(const struct chip_tsadc_table *table, int chn, void __iomem *reg, int temp); void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); + int (*get_trim_code)(const struct chip_tsadc_table *table, + int code, int trim_base, int trim_base_frac); /* Per-table methods */ struct chip_tsadc_table table; @@ -114,12 +122,16 @@ struct rockchip_tsadc_chip { * struct rockchip_thermal_sensor - hold the information of thermal sensor * @thermal: pointer to the platform/configuration data * @tzd: pointer to a thermal zone + * @of_node: pointer to the device_node representing this sensor, if any * @id: identifier of the thermal sensor + * @trim_temp: per-sensor trim temperature value */ struct rockchip_thermal_sensor { struct rockchip_thermal_data *thermal; struct thermal_zone_device *tzd; + struct device_node *of_node; int id; + int trim_temp; }; /** @@ -132,7 +144,11 @@ struct rockchip_thermal_sensor { * @pclk: the advanced peripherals bus clock * @grf: the general register file will be used to do static set by software * @regs: the base address of tsadc controller - * @tshut_temp: the hardware-controlled shutdown temperature value + * @trim_base: major component of sensor trim value, in Celsius + * @trim_base_frac: minor component of sensor trim value, in Decicelsius + * @trim: fallback thermal trim value for each channel + * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim + * @trim_temp: the fallback trim temperature for the whole sensor * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO) * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH) */ @@ -149,7 +165,12 @@ struct rockchip_thermal_data { struct regmap *grf; void __iomem *regs; + int trim_base; + int trim_base_frac; + int trim; + int tshut_temp; + int trim_temp; enum tshut_mode tshut_mode; enum tshut_polarity tshut_polarity; }; @@ -249,6 +270,9 @@ struct rockchip_thermal_data { #define GRF_CON_TSADC_CH_INV (0x10001 << 1) + +#define RK_MAX_TEMP (180000) + /** * struct tsadc_table - code to temperature conversion table * @code: the value of adc channel @@ -1061,6 +1085,15 @@ static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs, writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN); } +static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table, + int code, int trim_base, int trim_base_frac) +{ + int temp = trim_base * 1000 + trim_base_frac * 100; + u32 base_code = rk_tsadcv2_temp_to_code(table, temp); + + return code - base_code; +} + static const struct rockchip_tsadc_chip px30_tsadc_data = { /* cpu, gpu */ .chn_offset = 0, @@ -1298,6 +1331,8 @@ static const struct rockchip_tsadc_chip rk3576_tsadc_data = { .set_alarm_temp = rk_tsadcv3_alarm_temp, .set_tshut_temp = rk_tsadcv3_tshut_temp, .set_tshut_mode = rk_tsadcv4_tshut_mode, + .get_trim_code = rk_tsadcv2_get_trim_code, + .trim_slope = 923, .table = { .id = rk3588_code_table, .length = ARRAY_SIZE(rk3588_code_table), @@ -1413,7 +1448,7 @@ static int rockchip_thermal_set_trips(struct thermal_zone_device *tz, int low, i __func__, sensor->id, low, high); return tsadc->set_alarm_temp(&tsadc->table, - sensor->id, thermal->regs, high); + sensor->id, thermal->regs, high + sensor->trim_temp); } static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp) @@ -1425,6 +1460,8 @@ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_te retval = tsadc->get_temp(&tsadc->table, sensor->id, thermal->regs, out_temp); + *out_temp -= sensor->trim_temp; + return retval; } @@ -1433,6 +1470,104 @@ static const struct thermal_zone_device_ops rockchip_of_thermal_ops = { .set_trips = rockchip_thermal_set_trips, }; +/** + * rockchip_get_efuse_value - read an OTP cell from a device node + * @np: pointer to the device node with the nvmem-cells property + * @cell_name: name of cell that should be read + * @value: pointer to where the read value will be placed + * + * Return: Negative errno on failure, during which *value will not be touched, + * or 0 on success. + */ +static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name, + int *value) +{ + struct nvmem_cell *cell; + int ret = 0; + size_t len; + u8 *buf; + int i; + + cell = of_nvmem_cell_get(np, cell_name); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + if (len > sizeof(*value)) { + ret = -ERANGE; + goto exit; + } + + /* Copy with implicit endian conversion */ + *value = 0; + for (i = 0; i < len; i++) + *value |= (int) buf[i] << (8 * i); + +exit: + kfree(buf); + return ret; +} + +static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np, + struct rockchip_thermal_data *thermal) +{ + const struct rockchip_tsadc_chip *tsadc = thermal->chip; + int trim_base = 0, trim_base_frac = 0, trim = 0; + int trim_code; + int ret; + + thermal->trim_base = 0; + thermal->trim_base_frac = 0; + thermal->trim = 0; + + if (!tsadc->get_trim_code) + return 0; + + ret = rockchip_get_efuse_value(np, "trim_base", &trim_base); + if (ret < 0) { + if (ret == -ENOENT) { + trim_base = 30; + dev_dbg(dev, "trim_base is absent, defaulting to 30\n"); + } else { + dev_err(dev, "failed reading nvmem value of trim_base: %pe\n", + ERR_PTR(ret)); + return ret; + } + } + ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac); + if (ret < 0) { + if (ret == -ENOENT) { + dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n"); + } else { + dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n", + ERR_PTR(ret)); + return ret; + } + } + thermal->trim_base = trim_base; + thermal->trim_base_frac = trim_base_frac; + + /* + * If the tsadc node contains the trim property, then it is used in the + * absence of per-channel trim values + */ + if (!rockchip_get_efuse_value(np, "trim", &trim)) + thermal->trim = trim; + if (trim) { + trim_code = tsadc->get_trim_code(&tsadc->table, trim, + trim_base, trim_base_frac); + thermal->trim_temp = thermal->chip->trim_slope * trim_code; + } + + return 0; +} + static int rockchip_configure_from_dt(struct device *dev, struct device_node *np, struct rockchip_thermal_data *thermal) @@ -1493,6 +1628,8 @@ static int rockchip_configure_from_dt(struct device *dev, if (IS_ERR(thermal->grf)) dev_warn(dev, "Missing rockchip,grf property\n"); + rockchip_get_trim_configuration(dev, np, thermal); + return 0; } @@ -1503,23 +1640,50 @@ rockchip_thermal_register_sensor(struct platform_device *pdev, int id) { const struct rockchip_tsadc_chip *tsadc = thermal->chip; + struct device *dev = &pdev->dev; + int trim = thermal->trim; + int trim_code, tshut_temp; + int trim_temp = 0; int error; + if (thermal->trim_temp) + trim_temp = thermal->trim_temp; + + if (tsadc->get_trim_code && sensor->of_node) { + error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim); + if (error < 0 && error != -ENOENT) { + dev_err(dev, "failed reading trim of sensor %d: %pe\n", + id, ERR_PTR(error)); + return error; + } + if (trim) { + trim_code = tsadc->get_trim_code(&tsadc->table, trim, + thermal->trim_base, + thermal->trim_base_frac); + trim_temp = thermal->chip->trim_slope * trim_code; + } + } + + sensor->trim_temp = trim_temp; + + dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp); + + tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP); + tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode); - error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, - thermal->tshut_temp); + error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp); if (error) - dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n", - __func__, thermal->tshut_temp, error); + dev_err(dev, "%s: invalid tshut=%d, error=%d\n", + __func__, tshut_temp, error); sensor->thermal = thermal; sensor->id = id; - sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor, + sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor, &rockchip_of_thermal_ops); if (IS_ERR(sensor->tzd)) { error = PTR_ERR(sensor->tzd); - dev_err(&pdev->dev, "failed to register sensor %d: %d\n", + dev_err(dev, "failed to register sensor %d: %d\n", id, error); return error; } @@ -1542,9 +1706,11 @@ static int rockchip_thermal_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct rockchip_thermal_data *thermal; + struct device_node *child; int irq; int i; int error; + u32 chn; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -1595,6 +1761,18 @@ static int rockchip_thermal_probe(struct platform_device *pdev) thermal->chip->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity); + for_each_available_child_of_node(np, child) { + if (!of_property_read_u32(child, "reg", &chn)) { + if (chn < thermal->chip->chn_num) + thermal->sensors[chn].of_node = child; + else + dev_warn(&pdev->dev, + "sensor address (%d) too large, ignoring its trim\n", + chn); + } + + } + for (i = 0; i < thermal->chip->chn_num; i++) { error = rockchip_thermal_register_sensor(pdev, thermal, &thermal->sensors[i], @@ -1664,8 +1842,11 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev) static int __maybe_unused rockchip_thermal_resume(struct device *dev) { struct rockchip_thermal_data *thermal = dev_get_drvdata(dev); - int i; + const struct rockchip_tsadc_chip *tsadc = thermal->chip; + struct rockchip_thermal_sensor *sensor; + int tshut_temp; int error; + int i; error = clk_enable(thermal->clk); if (error) @@ -1679,21 +1860,23 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev) rockchip_thermal_reset_controller(thermal->reset); - thermal->chip->initialize(thermal->grf, thermal->regs, - thermal->tshut_polarity); + tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity); for (i = 0; i < thermal->chip->chn_num; i++) { - int id = thermal->sensors[i].id; + sensor = &thermal->sensors[i]; + + tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, + RK_MAX_TEMP); - thermal->chip->set_tshut_mode(id, thermal->regs, + tsadc->set_tshut_mode(sensor->id, thermal->regs, thermal->tshut_mode); - error = thermal->chip->set_tshut_temp(&thermal->chip->table, - id, thermal->regs, - thermal->tshut_temp); + error = tsadc->set_tshut_temp(&thermal->chip->table, + sensor->id, thermal->regs, + tshut_temp); if (error) dev_err(dev, "%s: invalid tshut=%d, error=%d\n", - __func__, thermal->tshut_temp, error); + __func__, tshut_temp, error); } thermal->chip->control(thermal->regs, true); -- GitLab From c5d5a72c01f7faabe7cc0fd63942c18372101daf Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Mon, 26 May 2025 18:26:57 +0800 Subject: [PATCH 771/868] thermal/drivers/mediatek/lvts_thermal: Change lvts commands array to static const Change the LVTS commands array to static const in preparation for adding different commands. Signed-off-by: Mason Chang Link: https://lore.kernel.org/r/20250526102659.30225-2-mason-cw.chang@mediatek.com Signed-off-by: Daniel Lezcano --- drivers/thermal/mediatek/lvts_thermal.c | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index acce8fde2cbad..40f9986fa36f1 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -96,6 +96,17 @@ #define LVTS_MINIMUM_THRESHOLD 20000 +static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 }; +/* + * Write device mask: 0xC1030000 + */ +static const u32 default_init_cmds[] = { + 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1, + 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300, + 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC, + 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1 +}; + static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT; static int golden_temp_offset; @@ -902,7 +913,7 @@ static void lvts_ctrl_monitor_enable(struct device *dev, struct lvts_ctrl *lvts_ * each write in the configuration register must be separated by a * delay of 2 us. */ -static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, u32 *cmds, int nr_cmds) +static void lvts_write_config(struct lvts_ctrl *lvts_ctrl, const u32 *cmds, int nr_cmds) { int i; @@ -985,9 +996,9 @@ static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable) static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl) { - u32 id, cmds[] = { 0xC103FFFF, 0xC502FF55 }; + u32 id; - lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds)); + lvts_write_config(lvts_ctrl, default_conn_cmds, ARRAY_SIZE(default_conn_cmds)); /* * LVTS_ID : Get ID and status of the thermal controller @@ -1006,17 +1017,7 @@ static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl) static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl) { - /* - * Write device mask: 0xC1030000 - */ - u32 cmds[] = { - 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1, - 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300, - 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC, - 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1 - }; - - lvts_write_config(lvts_ctrl, cmds, ARRAY_SIZE(cmds)); + lvts_write_config(lvts_ctrl, default_init_cmds, ARRAY_SIZE(default_init_cmds)); return 0; } -- GitLab From 6203a5e6fd090ed05f6d9b92e33bc7e7679a3dd6 Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Mon, 26 May 2025 18:26:58 +0800 Subject: [PATCH 772/868] thermal/drivers/mediatek/lvts_thermal: Add lvts commands and their sizes to driver data Add LVTS commands and their sizes to driver data in preparation for adding different commands. Signed-off-by: Mason Chang Link: https://lore.kernel.org/r/20250526102659.30225-3-mason-cw.chang@mediatek.com Signed-off-by: Daniel Lezcano --- drivers/thermal/mediatek/lvts_thermal.c | 65 ++++++++++++++++++++----- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 40f9986fa36f1..2b92e9b5d01ac 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -96,17 +96,6 @@ #define LVTS_MINIMUM_THRESHOLD 20000 -static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 }; -/* - * Write device mask: 0xC1030000 - */ -static const u32 default_init_cmds[] = { - 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1, - 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300, - 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC, - 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1 -}; - static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT; static int golden_temp_offset; @@ -136,7 +125,11 @@ struct lvts_ctrl_data { struct lvts_data { const struct lvts_ctrl_data *lvts_ctrl; + const u32 *conn_cmd; + const u32 *init_cmd; int num_lvts_ctrl; + int num_conn_cmd; + int num_init_cmd; int temp_factor; int temp_offset; int gt_calib_bit_offset; @@ -996,9 +989,10 @@ static int lvts_ctrl_set_enable(struct lvts_ctrl *lvts_ctrl, int enable) static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl) { + const struct lvts_data *lvts_data = lvts_ctrl->lvts_data; u32 id; - lvts_write_config(lvts_ctrl, default_conn_cmds, ARRAY_SIZE(default_conn_cmds)); + lvts_write_config(lvts_ctrl, lvts_data->conn_cmd, lvts_data->num_conn_cmd); /* * LVTS_ID : Get ID and status of the thermal controller @@ -1017,7 +1011,9 @@ static int lvts_ctrl_connect(struct device *dev, struct lvts_ctrl *lvts_ctrl) static int lvts_ctrl_initialize(struct device *dev, struct lvts_ctrl *lvts_ctrl) { - lvts_write_config(lvts_ctrl, default_init_cmds, ARRAY_SIZE(default_init_cmds)); + const struct lvts_data *lvts_data = lvts_ctrl->lvts_data; + + lvts_write_config(lvts_ctrl, lvts_data->init_cmd, lvts_data->num_init_cmd); return 0; } @@ -1446,6 +1442,17 @@ static int lvts_resume(struct device *dev) return 0; } +static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 }; +/* + * Write device mask: 0xC1030000 + */ +static const u32 default_init_cmds[] = { + 0xC1030E01, 0xC1030CFC, 0xC1030A8C, 0xC103098D, 0xC10308F1, + 0xC10307A6, 0xC10306B8, 0xC1030500, 0xC1030420, 0xC1030300, + 0xC1030030, 0xC10300F6, 0xC1030050, 0xC1030060, 0xC10300AC, + 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1 +}; + /* * The MT8186 calibration data is stored as packed 3-byte little-endian * values using a weird layout that makes sense only when viewed as a 32-bit @@ -1740,7 +1747,11 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { static const struct lvts_data mt7988_lvts_ap_data = { .lvts_ctrl = mt7988_lvts_ap_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, .gt_calib_bit_offset = 24, @@ -1748,7 +1759,11 @@ static const struct lvts_data mt7988_lvts_ap_data = { static const struct lvts_data mt8186_lvts_data = { .lvts_ctrl = mt8186_lvts_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8186_lvts_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, .gt_calib_bit_offset = 24, @@ -1757,7 +1772,11 @@ static const struct lvts_data mt8186_lvts_data = { static const struct lvts_data mt8188_lvts_mcu_data = { .lvts_ctrl = mt8188_lvts_mcu_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_mcu_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 20, @@ -1766,7 +1785,11 @@ static const struct lvts_data mt8188_lvts_mcu_data = { static const struct lvts_data mt8188_lvts_ap_data = { .lvts_ctrl = mt8188_lvts_ap_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8188_lvts_ap_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 20, @@ -1775,7 +1798,11 @@ static const struct lvts_data mt8188_lvts_ap_data = { static const struct lvts_data mt8192_lvts_mcu_data = { .lvts_ctrl = mt8192_lvts_mcu_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_mcu_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, @@ -1784,7 +1811,11 @@ static const struct lvts_data mt8192_lvts_mcu_data = { static const struct lvts_data mt8192_lvts_ap_data = { .lvts_ctrl = mt8192_lvts_ap_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8192_lvts_ap_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, @@ -1793,7 +1824,11 @@ static const struct lvts_data mt8192_lvts_ap_data = { static const struct lvts_data mt8195_lvts_mcu_data = { .lvts_ctrl = mt8195_lvts_mcu_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_mcu_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, @@ -1802,7 +1837,11 @@ static const struct lvts_data mt8195_lvts_mcu_data = { static const struct lvts_data mt8195_lvts_ap_data = { .lvts_ctrl = mt8195_lvts_ap_data_ctrl, + .conn_cmd = default_conn_cmds, + .init_cmd = default_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt8195_lvts_ap_data_ctrl), + .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), + .num_init_cmd = ARRAY_SIZE(default_init_cmds), .temp_factor = LVTS_COEFF_A_MT8195, .temp_offset = LVTS_COEFF_B_MT8195, .gt_calib_bit_offset = 24, -- GitLab From 685a755089f95b7e205c0202567d9a647f9de096 Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Mon, 26 May 2025 18:26:59 +0800 Subject: [PATCH 773/868] thermal/drivers/mediatek/lvts_thermal: Add mt7988 lvts commands These commands are necessary to avoid severely abnormal and inaccurate temperature readings that are caused by using the default commands. Signed-off-by: Mason Chang Link: https://lore.kernel.org/r/20250526102659.30225-4-mason-cw.chang@mediatek.com Signed-off-by: Daniel Lezcano --- drivers/thermal/mediatek/lvts_thermal.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 2b92e9b5d01ac..f4d1e66d7db9e 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -1443,6 +1443,8 @@ static int lvts_resume(struct device *dev) } static const u32 default_conn_cmds[] = { 0xC103FFFF, 0xC502FF55 }; +static const u32 mt7988_conn_cmds[] = { 0xC103FFFF, 0xC502FC55 }; + /* * Write device mask: 0xC1030000 */ @@ -1453,6 +1455,12 @@ static const u32 default_init_cmds[] = { 0xC10300FC, 0xC103009D, 0xC10300F1, 0xC10300E1 }; +static const u32 mt7988_init_cmds[] = { + 0xC1030300, 0xC1030420, 0xC1030500, 0xC10307A6, 0xC1030CFC, + 0xC1030A8C, 0xC103098D, 0xC10308F1, 0xC1030B04, 0xC1030E01, + 0xC10306B8 +}; + /* * The MT8186 calibration data is stored as packed 3-byte little-endian * values using a weird layout that makes sense only when viewed as a 32-bit @@ -1747,11 +1755,11 @@ static const struct lvts_ctrl_data mt8195_lvts_ap_data_ctrl[] = { static const struct lvts_data mt7988_lvts_ap_data = { .lvts_ctrl = mt7988_lvts_ap_data_ctrl, - .conn_cmd = default_conn_cmds, - .init_cmd = default_init_cmds, + .conn_cmd = mt7988_conn_cmds, + .init_cmd = mt7988_init_cmds, .num_lvts_ctrl = ARRAY_SIZE(mt7988_lvts_ap_data_ctrl), - .num_conn_cmd = ARRAY_SIZE(default_conn_cmds), - .num_init_cmd = ARRAY_SIZE(default_init_cmds), + .num_conn_cmd = ARRAY_SIZE(mt7988_conn_cmds), + .num_init_cmd = ARRAY_SIZE(mt7988_init_cmds), .temp_factor = LVTS_COEFF_A_MT7988, .temp_offset = LVTS_COEFF_B_MT7988, .gt_calib_bit_offset = 24, -- GitLab From 47f4bef6e71914fd0bab11ae0e3658802118f3d9 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Sun, 13 Jul 2025 10:05:30 +0200 Subject: [PATCH 774/868] dt-bindings: thermal: qcom-tsens: document the Milos Temperature Sensor Document the Temperature Sensor (TSENS) on the Milos SoC. Signed-off-by: Luca Weiss Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250713-sm7635-fp6-initial-v2-8-e8f9a789505b@fairphone.com Signed-off-by: Daniel Lezcano --- Documentation/devicetree/bindings/thermal/qcom-tsens.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml index 0e653bbe98849..94311ebd7652d 100644 --- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml +++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml @@ -49,6 +49,7 @@ properties: - description: v2 of TSENS items: - enum: + - qcom,milos-tsens - qcom,msm8953-tsens - qcom,msm8996-tsens - qcom,msm8998-tsens -- GitLab From db7897ad60fd7d7bf471bc1c49e3d01fefadade3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Jul 2025 15:03:55 +0200 Subject: [PATCH 775/868] misc: ti-fpc202: remove unneeded direction check As of commit 92ac7de3175e3 ("gpiolib: don't allow setting values on input lines"), the GPIO core makes sure values cannot be set on input lines. Remove the unnecessary check. Reviewed-by: Romain Gantois Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250717130357.53491-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/misc/ti_fpc202.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index f7cde245ac954..ca415ef45cbe7 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -125,9 +125,6 @@ static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, int ret; u8 val; - if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) - return; - ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); if (ret < 0) { dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); -- GitLab From 74896eae7e040e1b2a381efc8df0c839023dbf16 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Jul 2025 15:03:56 +0200 Subject: [PATCH 776/868] misc: ti-fpc202: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Reviewed-by: Romain Gantois Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250717130357.53491-2-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/misc/ti_fpc202.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index ca415ef45cbe7..f84cae50e2c9c 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -118,8 +118,8 @@ static void fpc202_set_enable(struct fpc202_priv *priv, int enable) gpiod_set_value(priv->en_gpio, enable); } -static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct fpc202_priv *priv = gpiochip_get_data(chip); int ret; @@ -128,7 +128,7 @@ static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); if (ret < 0) { dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); - return; + return ret; } val = (u8)ret; @@ -138,7 +138,7 @@ static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, else val &= ~BIT(offset - FPC202_GPIO_P0_S0_OUT_A); - fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val); + return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val); } static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -333,7 +333,7 @@ static int fpc202_probe(struct i2c_client *client) priv->gpio.base = -1; priv->gpio.direction_input = fpc202_gpio_direction_input; priv->gpio.direction_output = fpc202_gpio_direction_output; - priv->gpio.set = fpc202_gpio_set; + priv->gpio.set_rv = fpc202_gpio_set; priv->gpio.get = fpc202_gpio_get; priv->gpio.ngpio = FPC202_GPIO_COUNT; priv->gpio.parent = dev; -- GitLab From 906b955c60770bfdfae141aa993ae5fdb3eab193 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 17 Jul 2025 15:11:15 +0200 Subject: [PATCH 777/868] gpio: xilinx: convert set_multiple() to the new API as well The patch converting the driver to using new GPIO line value setters only converted the set() callback and missed set_multiple(). Fix it now. Fixes: 1919ea19a4ff ("gpio: xilinx: use new GPIO line value setter callbacks") Acked-by: Michal Simek Link: https://lore.kernel.org/r/20250717131116.53878-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xilinx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index aaaa741179805..36d91cacc2d96 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -175,8 +175,8 @@ static int xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) * This function writes the specified values into the specified signals of the * GPIO devices. */ -static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, - unsigned long *bits) +static int xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) { DECLARE_BITMAP(hw_mask, 64); DECLARE_BITMAP(hw_bits, 64); @@ -196,6 +196,8 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, bitmap_copy(chip->state, state, 64); raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; } /** @@ -605,7 +607,7 @@ static int xgpio_probe(struct platform_device *pdev) chip->gc.set_rv = xgpio_set; chip->gc.request = xgpio_request; chip->gc.free = xgpio_free; - chip->gc.set_multiple = xgpio_set_multiple; + chip->gc.set_multiple_rv = xgpio_set_multiple; chip->gc.label = dev_name(dev); -- GitLab From 2ae9b28947d486b7980b990cba205b1862b6d7a8 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:00 -0500 Subject: [PATCH 778/868] dt-bindings: gpio: Convert lacie,netxbig-gpio-ext to DT schema Convert the Lacie NetxBig GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202801.3010442-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/lacie,netxbig-gpio-ext.yaml | 60 +++++++++++++++++++ .../bindings/gpio/netxbig-gpio-ext.txt | 22 ------- 2 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt diff --git a/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml b/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml new file mode 100644 index 0000000000000..42021ee14125f --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/lacie,netxbig-gpio-ext.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/lacie,netxbig-gpio-ext.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NetxBig GPIO extension bus + +maintainers: + - Simon Guinot + +description: > + GPIO extension bus found on some LaCie/Seagate boards + (Example: 2Big/5Big Network v2, 2Big NAS). + +properties: + compatible: + items: + - const: lacie,netxbig-gpio-ext + + addr-gpios: + description: GPIOs representing the address register (LSB->MSB). + items: + - description: bit 0 (LSB) + - description: bit 1 + - description: bit 2 (MSB) + + data-gpios: + description: GPIOs representing the data register (LSB->MSB). + items: + - description: bit 0 (LSB) + - description: bit 1 + - description: bit 2 (MSB) + + enable-gpio: + description: Latches the new configuration (address, data) on raising edge. + maxItems: 1 + +required: + - compatible + - addr-gpios + - data-gpios + - enable-gpio + +additionalProperties: false + +examples: + - | + #include + + gpio { + compatible = "lacie,netxbig-gpio-ext"; + addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH + &gpio1 16 GPIO_ACTIVE_HIGH + &gpio1 17 GPIO_ACTIVE_HIGH>; + data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH + &gpio1 13 GPIO_ACTIVE_HIGH + &gpio1 14 GPIO_ACTIVE_HIGH>; + enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt b/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt deleted file mode 100644 index 50ec2e690701a..0000000000000 --- a/Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt +++ /dev/null @@ -1,22 +0,0 @@ -Binding for the GPIO extension bus found on some LaCie/Seagate boards -(Example: 2Big/5Big Network v2, 2Big NAS). - -Required properties: -- compatible: "lacie,netxbig-gpio-ext". -- addr-gpios: GPIOs representing the address register (LSB -> MSB). -- data-gpios: GPIOs representing the data register (LSB -> MSB). -- enable-gpio: latches the new configuration (address, data) on raising edge. - -Example: - -netxbig_gpio_ext: netxbig-gpio-ext { - compatible = "lacie,netxbig-gpio-ext"; - - addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH - &gpio1 16 GPIO_ACTIVE_HIGH - &gpio1 17 GPIO_ACTIVE_HIGH>; - data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH - &gpio1 13 GPIO_ACTIVE_HIGH - &gpio1 14 GPIO_ACTIVE_HIGH>; - enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>; -}; -- GitLab From 82388cb24a2c057316801dc390321cfe2bc0f3e2 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:06 -0500 Subject: [PATCH 779/868] dt-bindings: gpio: Convert microchip,pic32mzda-gpio to DT schema Convert the Microchip PIC32 GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202807.3010652-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/microchip,pic32-gpio.txt | 49 ------------- .../gpio/microchip,pic32mzda-gpio.yaml | 71 +++++++++++++++++++ 2 files changed, 71 insertions(+), 49 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt b/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt deleted file mode 100644 index dd031fc93b55c..0000000000000 --- a/Documentation/devicetree/bindings/gpio/microchip,pic32-gpio.txt +++ /dev/null @@ -1,49 +0,0 @@ -* Microchip PIC32 GPIO devices (PIO). - -Required properties: - - compatible: "microchip,pic32mzda-gpio" - - reg: Base address and length for the device. - - interrupts: The port interrupt shared by all pins. - - gpio-controller: Marks the port as GPIO controller. - - #gpio-cells: Two. The first cell is the pin number and - the second cell is used to specify the gpio polarity as defined in - defined in : - 0 = GPIO_ACTIVE_HIGH - 1 = GPIO_ACTIVE_LOW - 2 = GPIO_OPEN_DRAIN - - interrupt-controller: Marks the device node as an interrupt controller. - - #interrupt-cells: Two. The first cell is the GPIO number and second cell - is used to specify the trigger type as defined in - : - IRQ_TYPE_EDGE_RISING - IRQ_TYPE_EDGE_FALLING - IRQ_TYPE_EDGE_BOTH - - clocks: Clock specifier (see clock bindings for details). - - microchip,gpio-bank: Specifies which bank a controller owns. - - gpio-ranges: Interaction with the PINCTRL subsystem. - -Example: - -/* PORTA */ -gpio0: gpio0@1f860000 { - compatible = "microchip,pic32mzda-gpio"; - reg = <0x1f860000 0x100>; - interrupts = <118 IRQ_TYPE_LEVEL_HIGH>; - #gpio-cells = <2>; - gpio-controller; - interrupt-controller; - #interrupt-cells = <2>; - clocks = <&rootclk PB4CLK>; - microchip,gpio-bank = <0>; - gpio-ranges = <&pic32_pinctrl 0 0 16>; -}; - -keys { - ... - - button@sw1 { - label = "ESC"; - linux,code = <1>; - gpios = <&gpio0 12 0>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml new file mode 100644 index 0000000000000..d8d932c86697f --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/microchip,pic32mzda-gpio.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/microchip,pic32mzda-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PIC32 GPIO controller + +maintainers: + - Joshua Henderson + - Purna Chandra Mandal + +properties: + compatible: + const: microchip,pic32mzda-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + gpio-ranges: true + + "#gpio-cells": + const: 2 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + clocks: + maxItems: 1 + + microchip,gpio-bank: + description: Bank index owned by the controller + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - gpio-ranges + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + - clocks + - microchip,gpio-bank + +additionalProperties: false + +examples: + - | + #include + + gpio@1f860000 { + compatible = "microchip,pic32mzda-gpio"; + reg = <0x1f860000 0x100>; + interrupts = <118 IRQ_TYPE_LEVEL_HIGH>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&rootclk 11>; + microchip,gpio-bank = <0>; + gpio-ranges = <&pic32_pinctrl 0 0 16>; + }; -- GitLab From 98ce0e1c4a46049faf63bc02e1916bd219465974 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:12 -0500 Subject: [PATCH 780/868] dt-bindings: gpio: Convert exar,xra1403 to DT schema Convert XRA1403 16-bit GPIO Expander binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202813.3010879-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/exar,xra1403.yaml | 75 +++++++++++++++++++ .../devicetree/bindings/gpio/gpio-xra1403.txt | 46 ------------ 2 files changed, 75 insertions(+), 46 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/exar,xra1403.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-xra1403.txt diff --git a/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml b/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml new file mode 100644 index 0000000000000..053134faf475c --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/exar,xra1403.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/exar,xra1403.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: XRA1403 16-bit GPIO Expander with Reset Input + +maintainers: + - Nandor Han + +description: > + The XRA1403 is an 16-bit GPIO expander with an SPI interface. Features + available: + + - Individually programmable inputs: + - Internal pull-up resistors + - Polarity inversion + - Individual interrupt enable + - Rising edge and/or Falling edge interrupt + - Input filter + - Individually programmable outputs: + - Output Level Control + - Output Three-State Control + +properties: + compatible: + const: exar,xra1403 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + reset-gpios: + description: Control line for the device reset. + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@2 { + compatible = "exar,xra1403"; + reg = <2>; + spi-max-frequency = <1000000>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt b/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt deleted file mode 100644 index e13cc399b363f..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xra1403.txt +++ /dev/null @@ -1,46 +0,0 @@ -GPIO Driver for XRA1403 16-BIT GPIO Expander With Reset Input from EXAR - -The XRA1403 is an 16-bit GPIO expander with an SPI interface. Features available: - - Individually programmable inputs: - - Internal pull-up resistors - - Polarity inversion - - Individual interrupt enable - - Rising edge and/or Falling edge interrupt - - Input filter - - Individually programmable outputs - - Output Level Control - - Output Three-State Control - -Properties ----------- -Check documentation for SPI and GPIO controllers regarding properties needed to configure the node. - - - compatible = "exar,xra1403". - - reg - SPI id of the device. - - gpio-controller - marks the node as gpio. - - #gpio-cells - should be two where the first cell is the pin number - and the second one is used for optional parameters. - -Optional properties: -------------------- - - reset-gpios: in case available used to control the device reset line. - - interrupt-controller - marks the node as interrupt controller. - - #interrupt-cells - should be two and represents the number of cells - needed to encode interrupt source. - -Example --------- - - gpioxra0: gpio@2 { - compatible = "exar,xra1403"; - reg = <2>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - - reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; - spi-max-frequency = <1000000>; - }; -- GitLab From 5c163c9759605148d0c66acd8d795133c4b48ebb Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:29:25 -0500 Subject: [PATCH 781/868] dt-bindings: gpio: Convert cavium,octeon-3860-gpio to DT schema Convert the Cavium Octeon 3860 GPIO binding to DT schema format. It's a straight forward conversion. Looks like Octeon has no maintainers, so Bartosz is listed. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202927.3012974-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../gpio/cavium,octeon-3860-gpio.yaml | 62 +++++++++++++++++++ .../bindings/gpio/cavium-octeon-gpio.txt | 49 --------------- 2 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt diff --git a/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml b/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml new file mode 100644 index 0000000000000..35155b900655f --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/cavium,octeon-3860-gpio.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cavium,octeon-3860-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cavium Octeon 3860 GPIO controller + +maintainers: + - Bartosz Golaszewski + +properties: + compatible: + const: cavium,octeon-3860-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + + interrupts: + maxItems: 16 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - interrupt-controller + - '#interrupt-cells' + - interrupts + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + gpio@1070000000800 { + compatible = "cavium,octeon-3860-gpio"; + reg = <0x10700 0x00000800 0x0 0x100>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + /* The GPIO pin connect to 16 consecutive CUI bits */ + interrupts = <0 16>, <0 17>, <0 18>, <0 19>, + <0 20>, <0 21>, <0 22>, <0 23>, + <0 24>, <0 25>, <0 26>, <0 27>, + <0 28>, <0 29>, <0 30>, <0 31>; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt b/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt deleted file mode 100644 index 9d6dcd3fe7f99..0000000000000 --- a/Documentation/devicetree/bindings/gpio/cavium-octeon-gpio.txt +++ /dev/null @@ -1,49 +0,0 @@ -* General Purpose Input Output (GPIO) bus. - -Properties: -- compatible: "cavium,octeon-3860-gpio" - - Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. - -- reg: The base address of the GPIO unit's register bank. - -- gpio-controller: This is a GPIO controller. - -- #gpio-cells: Must be <2>. The first cell is the GPIO pin. - -- interrupt-controller: The GPIO controller is also an interrupt - controller, many of its pins may be configured as an interrupt - source. - -- #interrupt-cells: Must be <2>. The first cell is the GPIO pin - connected to the interrupt source. The second cell is the interrupt - triggering protocol and may have one of four values: - 1 - edge triggered on the rising edge. - 2 - edge triggered on the falling edge - 4 - level triggered active high. - 8 - level triggered active low. - -- interrupts: Interrupt routing for each pin. - -Example: - - gpio-controller@1070000000800 { - #gpio-cells = <2>; - compatible = "cavium,octeon-3860-gpio"; - reg = <0x10700 0x00000800 0x0 0x100>; - gpio-controller; - /* Interrupts are specified by two parts: - * 1) GPIO pin number (0..15) - * 2) Triggering (1 - edge rising - * 2 - edge falling - * 4 - level active high - * 8 - level active low) - */ - interrupt-controller; - #interrupt-cells = <2>; - /* The GPIO pin connect to 16 consecutive CUI bits */ - interrupts = <0 16>, <0 17>, <0 18>, <0 19>, - <0 20>, <0 21>, <0 22>, <0 23>, - <0 24>, <0 25>, <0 26>, <0 27>, - <0 28>, <0 29>, <0 30>, <0 31>; - }; -- GitLab From 7aee14a170a07bf6481f65e6a36a835e6414917e Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:29:17 -0500 Subject: [PATCH 782/868] dt-bindings: gpio: Convert cirrus,clps711x-mctrl-gpio to DT schema Convert the cirrus,clps711x-mctrl-gpio binding to DT schema format. Add the missing "gpio,syscon-dev" phandle property. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202919.3012764-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../gpio/cirrus,clps711x-mctrl-gpio.txt | 17 ------- .../gpio/cirrus,clps711x-mctrl-gpio.yaml | 49 +++++++++++++++++++ 2 files changed, 49 insertions(+), 17 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt deleted file mode 100644 index fd42e7280f721..0000000000000 --- a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.txt +++ /dev/null @@ -1,17 +0,0 @@ -* ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs - -Required properties: -- compatible: Should contain "cirrus,ep7209-mctrl-gpio". -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = Active high, - 1 = Active low. - -Example: - sysgpio: sysgpio { - compatible = "cirrus,ep7312-mctrl-gpio", - "cirrus,ep7209-mctrl-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml new file mode 100644 index 0000000000000..bdffca817f1be --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/cirrus,clps711x-mctrl-gpio.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/cirrus,clps711x-mctrl-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Cirrus Logic CLPS711X SYSFLG1 MCTRL GPIOs + +maintainers: + - Alexander Shiyan + +properties: + compatible: + oneOf: + - items: + - const: cirrus,ep7312-mctrl-gpio + - const: cirrus,ep7209-mctrl-gpio + - const: cirrus,ep7209-mctrl-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio,syscon-dev: + description: + Phandle and offset of device's specific registers within the syscon state + control registers + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon + - description: register offset within state control registers + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + sysgpio: sysgpio { + compatible = "cirrus,ep7312-mctrl-gpio", + "cirrus,ep7209-mctrl-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; -- GitLab From 695f375b2a881544d112edbb60a35a884c7604ae Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:29:04 -0500 Subject: [PATCH 783/868] dt-bindings: gpio: Convert altr,pio-1.0 to DT schema Convert the altr,pio-1.0 binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202905.3012386-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/altr-pio-1.0.yaml | 75 +++++++++++++++++++ .../devicetree/bindings/gpio/gpio-altera.txt | 44 ----------- 2 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-altera.txt diff --git a/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml b/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml new file mode 100644 index 0000000000000..18afed324198e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/altr-pio-1.0.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/altr-pio-1.0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Altera GPIO controller + +maintainers: + - Dinh Nguyen + - Marek Vasut + - Mathieu Malaterre + - Tien Hock Loh + +properties: + compatible: + const: altr,pio-1.0 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: + First cell is the GPIO offset number. Second cell is reserved and + currently unused. + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + altr,ngpio: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width of the GPIO bank. + default: 32 + + altr,interrupt-type: + $ref: /schemas/types.yaml#/definitions/uint32 + description: > + Specifies the interrupt trigger type synthesized by hardware. + Values defined in . + enum: [1, 2, 3, 4] + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + +additionalProperties: false + +examples: + - | + #include + + gpio@ff200000 { + compatible = "altr,pio-1.0"; + reg = <0xff200000 0x10>; + interrupts = <45 4>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + altr,ngpio = <32>; + altr,interrupt-type = ; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt deleted file mode 100644 index 2a80e272cd666..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-altera.txt +++ /dev/null @@ -1,44 +0,0 @@ -Altera GPIO controller bindings - -Required properties: -- compatible: - - "altr,pio-1.0" -- reg: Physical base address and length of the controller's registers. -- #gpio-cells : Should be 2 - - The first cell is the gpio offset number. - - The second cell is reserved and is currently unused. -- gpio-controller : Marks the device node as a GPIO controller. -- interrupt-controller: Mark the device node as an interrupt controller -- #interrupt-cells : Should be 2. The interrupt type is fixed in the hardware. - - The first cell is the GPIO offset number within the GPIO controller. - - The second cell is the interrupt trigger type and level flags. -- interrupts: Specify the interrupt. -- altr,interrupt-type: Specifies the interrupt trigger type the GPIO - hardware is synthesized. This field is required if the Altera GPIO controller - used has IRQ enabled as the interrupt type is not software controlled, - but hardware synthesized. Required if GPIO is used as an interrupt - controller. The value is defined in - Only the following flags are supported: - IRQ_TYPE_EDGE_RISING - IRQ_TYPE_EDGE_FALLING - IRQ_TYPE_EDGE_BOTH - IRQ_TYPE_LEVEL_HIGH - -Optional properties: -- altr,ngpio: Width of the GPIO bank. This defines how many pins the - GPIO device has. Ranges between 1-32. Optional and defaults to 32 if not - specified. - -Example: - -gpio_altr: gpio@ff200000 { - compatible = "altr,pio-1.0"; - reg = <0xff200000 0x10>; - interrupts = <0 45 4>; - altr,ngpio = <32>; - altr,interrupt-type = ; - #gpio-cells = <2>; - gpio-controller; - #interrupt-cells = <2>; - interrupt-controller; -}; -- GitLab From aff0a1701b020c8e6b172f28828fd4f3e6eed41a Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:49 -0500 Subject: [PATCH 784/868] dt-bindings: gpio: Convert ti,keystone-dsp-gpio to DT schema Convert the TI Keystone DSP GPIO binding to DT schema format. The "ti,syscon-dev" property was wrong and should be "gpio,syscon-dev" instead. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202850.3011952-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/gpio-dsp-keystone.txt | 39 ----------- .../bindings/gpio/ti,keystone-dsp-gpio.yaml | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+), 39 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt create mode 100644 Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt b/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt deleted file mode 100644 index 0423699d74c77..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-dsp-keystone.txt +++ /dev/null @@ -1,39 +0,0 @@ -Keystone 2 DSP GPIO controller bindings - -HOST OS userland running on ARM can send interrupts to DSP cores using -the DSP GPIO controller IP. It provides 28 IRQ signals per each DSP core. -This is one of the component used by the IPC mechanism used on Keystone SOCs. - -For example TCI6638K2K SoC has 8 DSP GPIO controllers: - - 8 for C66x CorePacx CPUs 0-7 - -Keystone 2 DSP GPIO controller has specific features: -- each GPIO can be configured only as output pin; -- setting GPIO value to 1 causes IRQ generation on target DSP core; -- reading pin value returns 0 - if IRQ was handled or 1 - IRQ is still - pending. - -Required Properties: -- compatible: should be "ti,keystone-dsp-gpio" -- ti,syscon-dev: phandle/offset pair. The phandle to syscon used to - access device state control registers and the offset of device's specific - registers within device state control registers range. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be 2. - -Please refer to gpio.txt in this directory for details of the common GPIO -bindings used by client devices. - -Example: - dspgpio0: keystone_dsp_gpio@2620240 { - compatible = "ti,keystone-dsp-gpio"; - ti,syscon-dev = <&devctrl 0x240>; - gpio-controller; - #gpio-cells = <2>; - }; - - dsp0: dsp0 { - compatible = "linux,rproc-user"; - ... - kick-gpio = <&dspgpio0 27>; - }; diff --git a/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml b/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml new file mode 100644 index 0000000000000..59f81621408be --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/ti,keystone-dsp-gpio.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/ti,keystone-dsp-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Keystone 2 DSP GPIO controller + +maintainers: + - Grygorii Strashko + +description: | + HOST OS userland running on ARM can send interrupts to DSP cores using + the DSP GPIO controller IP. It provides 28 IRQ signals per each DSP core. + This is one of the component used by the IPC mechanism used on Keystone SOCs. + + For example TCI6638K2K SoC has 8 DSP GPIO controllers: + - 8 for C66x CorePacx CPUs 0-7 + + Keystone 2 DSP GPIO controller has specific features: + - each GPIO can be configured only as output pin; + - setting GPIO value to 1 causes IRQ generation on target DSP core; + - reading pin value returns 0 - if IRQ was handled or 1 - IRQ is still + pending. + +properties: + compatible: + const: ti,keystone-dsp-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio,syscon-dev: + description: + Phandle and offset of device's specific registers within the syscon state + control registers + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon + - description: register offset within state control registers + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - gpio,syscon-dev + +additionalProperties: false + +examples: + - | + gpio@240 { + compatible = "ti,keystone-dsp-gpio"; + reg = <0x240 0x4>; + gpio-controller; + #gpio-cells = <2>; + gpio,syscon-dev = <&devctrl 0x240>; + }; -- GitLab From 71b660010bde67a0e0ffdee67d30e62672e6d393 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:35 -0500 Subject: [PATCH 785/868] dt-bindings: gpio: Convert lantiq,gpio-mm-lantiq to DT schema Convert the Lantiq SoC External Bus memory mapped GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202837.3011521-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/gpio-mm-lantiq.txt | 38 ------------- .../bindings/gpio/lantiq,gpio-mm-lantiq.yaml | 54 +++++++++++++++++++ 2 files changed, 54 insertions(+), 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt create mode 100644 Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt b/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt deleted file mode 100644 index f93d51478d5ab..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-mm-lantiq.txt +++ /dev/null @@ -1,38 +0,0 @@ -Lantiq SoC External Bus memory mapped GPIO controller - -By attaching hardware latches to the EBU it is possible to create output -only gpios. This driver configures a special memory address, which when -written to outputs 16 bit to the latches. - -The node describing the memory mapped GPIOs needs to be a child of the node -describing the "lantiq,localbus". - -Required properties: -- compatible : Should be "lantiq,gpio-mm-lantiq" -- reg : Address and length of the register set for the device -- #gpio-cells : Should be two. The first cell is the pin number and - the second cell is used to specify optional parameters (currently - unused). -- gpio-controller : Marks the device node as a gpio controller. - -Optional properties: -- lantiq,shadow : The default value that we shall assume as already set on the - shift register cascade. - -Example: - -localbus@0 { - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x0 0x3ffffff /* addrsel0 */ - 1 0 0x4000000 0x4000010>; /* addsel1 */ - compatible = "lantiq,localbus", "simple-bus"; - - gpio_mm0: gpio@4000000 { - compatible = "lantiq,gpio-mm"; - reg = <1 0x0 0x10>; - gpio-controller; - #gpio-cells = <2>; - lantiq,shadow = <0x77f> - }; -} diff --git a/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml b/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml new file mode 100644 index 0000000000000..eaf53a89542a7 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/lantiq,gpio-mm-lantiq.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/lantiq,gpio-mm-lantiq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq SoC External Bus memory mapped GPIO controller + +maintainers: + - John Crispin + +description: | + By attaching hardware latches to the EBU it is possible to create output + only gpios. This driver configures a special memory address, which when + written to outputs 16 bit to the latches. + + The node describing the memory mapped GPIOs needs to be a child of the node + describing the "lantiq,localbus". + +properties: + compatible: + enum: + - lantiq,gpio-mm-lantiq + - lantiq,gpio-mm + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + lantiq,shadow: + description: The default value that we shall assume as already set on the shift register cascade. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - '#gpio-cells' + - gpio-controller + +additionalProperties: false + +examples: + - | + gpio@4000000 { + compatible = "lantiq,gpio-mm-lantiq"; + reg = <0x4000000 0x10>; + gpio-controller; + #gpio-cells = <2>; + lantiq,shadow = <0x77f>; + }; -- GitLab From 842dcff8e2d6fb33f7e9d59332a713b867a8f187 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:28 -0500 Subject: [PATCH 786/868] dt-bindings: gpio: Convert ti,twl4030-gpio to DT schema Convert the TI TWL4030 PMIC GPIO binding to DT schema format. The number of #interrupt-cells was wrong compared to what is in use. Correct it to be 1. Reviewed-by: Andreas Kemnade Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202829.3011298-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-twl4030.txt | 29 --------- .../bindings/gpio/ti,twl4030-gpio.yaml | 61 +++++++++++++++++++ 2 files changed, 61 insertions(+), 29 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-twl4030.txt create mode 100644 Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt b/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt deleted file mode 100644 index 66788fda1db38..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-twl4030.txt +++ /dev/null @@ -1,29 +0,0 @@ -twl4030 GPIO controller bindings - -Required properties: -- compatible: - - "ti,twl4030-gpio" for twl4030 GPIO controller -- #gpio-cells : Should be two. - - first cell is the pin number - - second cell is used to specify optional parameters (unused) -- gpio-controller : Marks the device node as a GPIO controller. -- #interrupt-cells : Should be 2. -- interrupt-controller: Mark the device node as an interrupt controller - The first cell is the GPIO number. - The second cell is not used. -- ti,use-leds : Enables LEDA and LEDB outputs if set -- ti,debounce : if n-th bit is set, debounces GPIO-n -- ti,mmc-cd : if n-th bit is set, GPIO-n controls VMMC(n+1) -- ti,pullups : if n-th bit is set, set a pullup on GPIO-n -- ti,pulldowns : if n-th bit is set, set a pulldown on GPIO-n - -Example: - -twl_gpio: gpio { - compatible = "ti,twl4030-gpio"; - #gpio-cells = <2>; - gpio-controller; - #interrupt-cells = <2>; - interrupt-controller; - ti,use-leds; -}; diff --git a/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml new file mode 100644 index 0000000000000..5e3e199fd9a49 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ti,twl4030-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI TWL4030 GPIO controller + +maintainers: + - Aaro Koskinen + - Andreas Kemnade + - Kevin Hilman + - Roger Quadros + - Tony Lindgren + +properties: + compatible: + const: ti,twl4030-gpio + + '#gpio-cells': + const: 2 + + gpio-controller: true + + '#interrupt-cells': + const: 1 + + interrupt-controller: true + + ti,debounce: + description: Debounce control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,mmc-cd: + description: MMC card detect control bits. Each bit corresponds to a GPIO pin for VMMC(n+1). + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,pullups: + description: Pull-up control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,pulldowns: + description: Pull-down control bits. Each bit corresponds to a GPIO pin. + $ref: /schemas/types.yaml#/definitions/uint32 + + ti,use-leds: + type: boolean + description: Enables LEDA and LEDB outputs if set + +additionalProperties: false + +examples: + - | + gpio { + compatible = "ti,twl4030-gpio"; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <1>; + interrupt-controller; + ti,use-leds; + }; -- GitLab From 672d644a7da94ee906950953188c6da26ae99594 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:20 -0500 Subject: [PATCH 787/868] dt-bindings: gpio: Convert apm,xgene-gpio-sb to DT schema Convert APM X-Gene Standby GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202821.3011099-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/apm,xgene-gpio-sb.yaml | 94 +++++++++++++++++++ .../bindings/gpio/gpio-xgene-sb.txt | 64 ------------- 2 files changed, 94 insertions(+), 64 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt diff --git a/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml b/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml new file mode 100644 index 0000000000000..d205dd7b492cb --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/apm,xgene-gpio-sb.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/apm,xgene-gpio-sb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene Standby GPIO controller + +maintainers: + - Khuong Dinh + +description: | + This is a gpio controller in the standby domain. It also supports interrupt in + some particular pins which are sourced to its parent interrupt controller + as diagram below: + +-----------------+ + | X-Gene standby | + | GPIO controller +------ GPIO_0 + +------------+ | | ... + | Parent IRQ | EXT_INT_0 | +------ GPIO_8/EXT_INT_0 + | controller | (SPI40) | | ... + | (GICv2) +--------------+ +------ GPIO_[N+8]/EXT_INT_N + | | ... | | + | | EXT_INT_N | +------ GPIO_[N+9] + | | (SPI[40 + N])| | ... + | +--------------+ +------ GPIO_MAX + +------------+ +-----------------+ + +properties: + compatible: + const: apm,xgene-gpio-sb + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + interrupts: + description: + List of interrupt specifiers for EXT_INT_0 through EXT_INT_N. The first + entry must correspond to EXT_INT_0. + + '#interrupt-cells': + const: 2 + description: + First cell selects EXT_INT_N (0-N), second cell specifies flags + + interrupt-controller: true + + apm,nr-gpios: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of GPIO pins + + apm,nr-irqs: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of interrupt pins + + apm,irq-start: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Lowest GPIO pin supporting interrupts + +required: + - compatible + - reg + - '#gpio-cells' + - gpio-controller + - interrupts + - '#interrupt-cells' + - interrupt-controller + +additionalProperties: false + +examples: + - | + gpio@17001000 { + compatible = "apm,xgene-gpio-sb"; + reg = <0x17001000 0x400>; + #gpio-cells = <2>; + gpio-controller; + interrupts = <0x0 0x28 0x1>, + <0x0 0x29 0x1>, + <0x0 0x2a 0x1>, + <0x0 0x2b 0x1>, + <0x0 0x2c 0x1>, + <0x0 0x2d 0x1>; + #interrupt-cells = <2>; + interrupt-controller; + apm,nr-gpios = <22>; + apm,nr-irqs = <6>; + apm,irq-start = <8>; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt deleted file mode 100644 index 7ddf292db1444..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xgene-sb.txt +++ /dev/null @@ -1,64 +0,0 @@ -APM X-Gene Standby GPIO controller bindings - -This is a gpio controller in the standby domain. It also supports interrupt in -some particular pins which are sourced to its parent interrupt controller -as diagram below: - +-----------------+ - | X-Gene standby | - | GPIO controller +------ GPIO_0 -+------------+ | | ... -| Parent IRQ | EXT_INT_0 | +------ GPIO_8/EXT_INT_0 -| controller | (SPI40) | | ... -| (GICv2) +--------------+ +------ GPIO_[N+8]/EXT_INT_N -| | ... | | -| | EXT_INT_N | +------ GPIO_[N+9] -| | (SPI[40 + N])| | ... -| +--------------+ +------ GPIO_MAX -+------------+ +-----------------+ - -Required properties: -- compatible: "apm,xgene-gpio-sb" for the X-Gene Standby GPIO controller -- reg: Physical base address and size of the controller's registers -- #gpio-cells: Should be two. - - first cell is the pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller: Marks the device node as a GPIO controller. -- interrupts: The EXT_INT_0 parent interrupt resource must be listed first. -- interrupt-cells: Should be two. - - first cell is 0-N corresponding for EXT_INT_0 to EXT_INT_N. - - second cell is used to specify flags. -- interrupt-controller: Marks the device node as an interrupt controller. -- apm,nr-gpios: Optional, specify number of gpios pin. -- apm,nr-irqs: Optional, specify number of interrupt pins. -- apm,irq-start: Optional, specify lowest gpio pin support interrupt. - -Example: - sbgpio: gpio@17001000{ - compatible = "apm,xgene-gpio-sb"; - reg = <0x0 0x17001000 0x0 0x400>; - #gpio-cells = <2>; - gpio-controller; - interrupts = <0x0 0x28 0x1>, - <0x0 0x29 0x1>, - <0x0 0x2a 0x1>, - <0x0 0x2b 0x1>, - <0x0 0x2c 0x1>, - <0x0 0x2d 0x1>; - interrupt-parent = <&gic>; - #interrupt-cells = <2>; - interrupt-controller; - apm,nr-gpios = <22>; - apm,nr-irqs = <6>; - apm,irq-start = <8>; - }; - - testuser { - compatible = "example,testuser"; - /* Use the GPIO_13/EXT_INT_5 line as an active high triggered - * level interrupt - */ - interrupts = <5 4>; - interrupt-parent = <&sbgpio>; - }; -- GitLab From 48a9cf93ba3a11dab608a0ea11341ccda0494e3d Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:29:32 -0500 Subject: [PATCH 788/868] dt-bindings: gpio: Convert abilis,tb10x-gpio to DT schema Convert the Abilis TB10x GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202934.3013189-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/abilis,tb10x-gpio.txt | 35 ----------- .../bindings/gpio/abilis,tb10x-gpio.yaml | 63 +++++++++++++++++++ 2 files changed, 63 insertions(+), 35 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt deleted file mode 100644 index ce19c5660aca4..0000000000000 --- a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Abilis TB10x GPIO controller - -Required Properties: -- compatible: Should be "abilis,tb10x-gpio" -- reg: Address and length of the register set for the device -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be <2>. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted). -- abilis,ngpio: the number of GPIO pins this driver controls. - -Optional Properties: -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: Should be <1>. Interrupts are triggered on both edges. -- interrupts: Defines the interrupt line connecting this GPIO controller to - its parent interrupt controller. - -GPIO ranges are specified as described in -Documentation/devicetree/bindings/gpio/gpio.txt - -Example: - - gpioa: gpio@ff140000 { - compatible = "abilis,tb10x-gpio"; - interrupt-controller; - #interrupt-cells = <1>; - interrupt-parent = <&tb10x_ictl>; - interrupts = <27 2>; - reg = <0xFF140000 0x1000>; - gpio-controller; - #gpio-cells = <2>; - abilis,ngpio = <3>; - gpio-ranges = <&iomux 0 0 0>; - gpio-ranges-group-names = "gpioa_pins"; - }; diff --git a/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml new file mode 100644 index 0000000000000..c93ec0f16bcd2 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/abilis,tb10x-gpio.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/abilis,tb10x-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Abilis TB10x GPIO controller + +maintainers: + - Christian Ruppert + +properties: + compatible: + const: abilis,tb10x-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + gpio-ranges: true + + gpio-ranges-group-names: true + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + description: Interrupts are triggered on both edges + + interrupts: + maxItems: 1 + + abilis,ngpio: + description: Number of GPIO pins this driver controls + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - abilis,ngpio + +additionalProperties: false + +examples: + - | + gpio@ff140000 { + compatible = "abilis,tb10x-gpio"; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <27 2>; + reg = <0xff140000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + abilis,ngpio = <3>; + gpio-ranges = <&iomux 0 0 0>; + gpio-ranges-group-names = "gpioa_pins"; + }; -- GitLab From aa66eb1202d65923e101c7b50d757ddf0422eb27 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:27:52 -0500 Subject: [PATCH 789/868] dt-bindings: gpio: Convert st,spear-spics-gpio to DT schema Convert the ST SPEAr SPI CS GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20250714202753.3010240-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/spear_spics.txt | 49 ----------- .../bindings/gpio/st,spear-spics-gpio.yaml | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 49 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/spear_spics.txt create mode 100644 Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/spear_spics.txt b/Documentation/devicetree/bindings/gpio/spear_spics.txt deleted file mode 100644 index dd04d96e6ff15..0000000000000 --- a/Documentation/devicetree/bindings/gpio/spear_spics.txt +++ /dev/null @@ -1,49 +0,0 @@ -=== ST Microelectronics SPEAr SPI CS Driver === - -SPEAr platform provides a provision to control chipselects of ARM PL022 Prime -Cell spi controller through its system registers, which otherwise remains under -PL022 control. If chipselect remain under PL022 control then they would be -released as soon as transfer is over and TxFIFO becomes empty. This is not -desired by some of the device protocols above spi which expect (multiple) -transfers without releasing their chipselects. - -Chipselects can be controlled by software by turning them as GPIOs. SPEAr -provides another interface through system registers through which software can -directly control each PL022 chipselect. Hence, it is natural for SPEAr to export -the control of this interface as gpio. - -Required properties: - - * compatible: should be defined as "st,spear-spics-gpio" - * reg: mentioning address range of spics controller - * st-spics,peripcfg-reg: peripheral configuration register offset - * st-spics,sw-enable-bit: bit offset to enable sw control - * st-spics,cs-value-bit: bit offset to drive chipselect low or high - * st-spics,cs-enable-mask: chip select number bit mask - * st-spics,cs-enable-shift: chip select number program offset - * gpio-controller: Marks the device node as gpio controller - * #gpio-cells: should be 1 and will mention chip select number - -All the above bit offsets are within peripcfg register. - -Example: -------- -spics: spics@e0700000{ - compatible = "st,spear-spics-gpio"; - reg = <0xe0700000 0x1000>; - st-spics,peripcfg-reg = <0x3b0>; - st-spics,sw-enable-bit = <12>; - st-spics,cs-value-bit = <11>; - st-spics,cs-enable-mask = <3>; - st-spics,cs-enable-shift = <8>; - gpio-controller; - #gpio-cells = <2>; -}; - - -spi0: spi@e0100000 { - num-cs = <3>; - cs-gpios = <&gpio1 7 0>, <&spics 0>, - <&spics 1>; - ... -} diff --git a/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml b/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml new file mode 100644 index 0000000000000..3b0d2112da790 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/st,spear-spics-gpio.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/st,spear-spics-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST Microelectronics SPEAr SPI CS GPIO Controller + +maintainers: + - Viresh Kumar + +description: > + SPEAr platform provides a provision to control chipselects of ARM PL022 Prime + Cell spi controller through its system registers, which otherwise remains + under PL022 control. If chipselect remain under PL022 control then they would + be released as soon as transfer is over and TxFIFO becomes empty. This is not + desired by some of the device protocols above spi which expect (multiple) + transfers without releasing their chipselects. + + Chipselects can be controlled by software by turning them as GPIOs. SPEAr + provides another interface through system registers through which software can + directly control each PL022 chipselect. Hence, it is natural for SPEAr to + export the control of this interface as gpio. + +properties: + compatible: + const: st,spear-spics-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + st-spics,peripcfg-reg: + description: Offset of the peripcfg register. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,sw-enable-bit: + description: Bit offset to enable software chipselect control. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-value-bit: + description: Bit offset to drive chipselect low or high. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-enable-mask: + description: Bitmask selecting which chipselects to enable. + $ref: /schemas/types.yaml#/definitions/uint32 + + st-spics,cs-enable-shift: + description: Bit shift for programming chipselect number. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - st-spics,peripcfg-reg + - st-spics,sw-enable-bit + - st-spics,cs-value-bit + - st-spics,cs-enable-mask + - st-spics,cs-enable-shift + +additionalProperties: false + +examples: + - | + gpio@e0700000 { + compatible = "st,spear-spics-gpio"; + reg = <0xe0700000 0x1000>; + st-spics,peripcfg-reg = <0x3b0>; + st-spics,sw-enable-bit = <12>; + st-spics,cs-value-bit = <11>; + st-spics,cs-enable-mask = <3>; + st-spics,cs-enable-shift = <8>; + gpio-controller; + #gpio-cells = <2>; + }; -- GitLab From f03a7f20b23c1abb1066f791806bbca406362324 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:19:51 -0500 Subject: [PATCH 790/868] dt-bindings: gpio: Create a trivial GPIO schema Many simple GPIO controllers without interrupt capability have the same schema other than their compatible value. Combine all these bindings into a single schema. The criteria to be included here is must use 2 cells, have no interrupt capability, have 0 or 1 "reg" entries, and have no other resources (like clocks). Note that "ngpios" is now allowed in some cases it wasn't before and constraints on it have been dropped. Signed-off-by: Rob Herring (Arm) Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250714201959.2983482-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/fcs,fxl6408.yaml | 59 ---------- .../bindings/gpio/gateworks,pld-gpio.txt | 19 --- .../bindings/gpio/gpio-clps711x.txt | 28 ----- .../devicetree/bindings/gpio/gpio-lp3943.txt | 37 ------ .../bindings/gpio/gpio-max77620.txt | 25 ---- .../devicetree/bindings/gpio/gpio-moxtet.txt | 18 --- .../devicetree/bindings/gpio/gpio-palmas.txt | 27 ----- .../bindings/gpio/gpio-pca9570.yaml | 56 --------- .../bindings/gpio/gpio-tpic2810.yaml | 51 -------- .../devicetree/bindings/gpio/gpio-ts4800.txt | 20 ---- .../devicetree/bindings/gpio/gpio-ts4900.txt | 30 ----- .../devicetree/bindings/gpio/gpio-xgene.txt | 22 ---- .../bindings/gpio/ibm,ppc4xx-gpio.txt | 24 ---- .../bindings/gpio/loongson,ls1x-gpio.yaml | 49 -------- .../bindings/gpio/nintendo,hollywood-gpio.txt | 26 ----- .../gpio/rockchip,rk3328-grf-gpio.yaml | 50 -------- .../bindings/gpio/snps,creg-gpio.txt | 21 ---- .../devicetree/bindings/gpio/ti,7416374.yaml | 56 --------- .../bindings/gpio/trivial-gpio.yaml | 110 ++++++++++++++++++ .../devicetree/bindings/mfd/lp3943.txt | 2 +- .../bindings/powerpc/nintendo/wii.txt | 4 - .../devicetree/bindings/soc/rockchip/grf.yaml | 8 +- MAINTAINERS | 3 - 23 files changed, 116 insertions(+), 629 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-clps711x.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-lp3943.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-max77620.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-moxtet.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-palmas.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-ts4800.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-ts4900.txt delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-xgene.txt delete mode 100644 Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt delete mode 100644 Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt delete mode 100644 Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt delete mode 100644 Documentation/devicetree/bindings/gpio/ti,7416374.yaml create mode 100644 Documentation/devicetree/bindings/gpio/trivial-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml b/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml deleted file mode 100644 index b74fa81e7d05b..0000000000000 --- a/Documentation/devicetree/bindings/gpio/fcs,fxl6408.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/fcs,fxl6408.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Fairchild FXL6408 I2C GPIO Expander - -maintainers: - - Emanuele Ghidoli - -properties: - compatible: - enum: - - fcs,fxl6408 - - reg: - maxItems: 1 - - "#gpio-cells": - const: 2 - - gpio-controller: true - - gpio-line-names: - minItems: 1 - maxItems: 8 - -patternProperties: - "^(hog-[0-9]+|.+-hog(-[0-9]+)?)$": - type: object - required: - - gpio-hog - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - gpio_expander_43: gpio-expander@43 { - compatible = "fcs,fxl6408"; - reg = <0x43>; - gpio-controller; - #gpio-cells = <2>; - gpio-line-names = "Wi-Fi_W_DISABLE", "Wi-Fi_WKUP_WLAN", - "PWR_EN_+V3.3_WiFi_N", "PCIe_REF_CLK_EN", - "USB_RESET_N", "USB_BYPASS_N", "Wi-Fi_PDn", - "Wi-Fi_WKUP_BT"; - }; - }; diff --git a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt b/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt deleted file mode 100644 index d543fd1b8b23e..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gateworks,pld-gpio.txt +++ /dev/null @@ -1,19 +0,0 @@ -Gateworks PLD GPIO controller bindings - -The GPIO controller should be a child node on an I2C bus. - -Required properties: -- compatible: Should be "gateworks,pld-gpio" -- reg: I2C slave address -- gpio-controller: Marks the device node as a GPIO controller. -- #gpio-cells: Should be <2>. The first cell is the gpio number and - the second cell is used to specify optional parameters. - -Example: - -pld@56 { - compatible = "gateworks,pld-gpio"; - reg = <0x56>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt b/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt deleted file mode 100644 index 0a304ad29d817..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-clps711x.txt +++ /dev/null @@ -1,28 +0,0 @@ -Cirrus Logic CLPS711X GPIO controller - -Required properties: -- compatible: Should be "cirrus,ep7209-gpio" -- reg: Physical base GPIO controller registers location and length. - There should be two registers, first is DATA register, the second - is DIRECTION. -- gpio-controller: Marks the device node as a gpio controller. -- #gpio-cells: Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low - -Note: Each GPIO port should have an alias correctly numbered in "aliases" -node. - -Example: - -aliases { - gpio0 = &porta; -}; - -porta: gpio@80000000 { - compatible = "cirrus,ep7312-gpio","cirrus,ep7209-gpio"; - reg = <0x80000000 0x1>, <0x80000040 0x1>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt b/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt deleted file mode 100644 index 80fcb7d70e13d..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-lp3943.txt +++ /dev/null @@ -1,37 +0,0 @@ -TI/National Semiconductor LP3943 GPIO controller - -Required properties: - - compatible: "ti,lp3943-gpio" - - gpio-controller: Marks the device node as a GPIO controller. - - #gpio-cells: Should be 2. See gpio.txt in this directory for a - description of the cells format. - -Example: -Simple LED controls with LP3943 GPIO controller - -&i2c4 { - lp3943@60 { - compatible = "ti,lp3943"; - reg = <0x60>; - - gpioex: gpio { - compatible = "ti,lp3943-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - }; -}; - -leds { - compatible = "gpio-leds"; - indicator1 { - label = "indi1"; - gpios = <&gpioex 9 GPIO_ACTIVE_LOW>; - }; - - indicator2 { - label = "indi2"; - gpios = <&gpioex 10 GPIO_ACTIVE_LOW>; - default-state = "off"; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-max77620.txt b/Documentation/devicetree/bindings/gpio/gpio-max77620.txt deleted file mode 100644 index 410e716fd3d22..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-max77620.txt +++ /dev/null @@ -1,25 +0,0 @@ -GPIO driver for MAX77620 Power management IC from Maxim Semiconductor. - -Device has 8 GPIO pins which can be configured as GPIO as well as the -special IO functions. - -Required properties: -------------------- -- gpio-controller : Marks the device node as a gpio controller. -- #gpio-cells : Should be two. The first cell is the pin number and - the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -For more details, please refer generic GPIO DT binding document -. - -Example: --------- -#include -... -max77620@3c { - compatible = "maxim,max77620"; - - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt b/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt deleted file mode 100644 index 410759de9f095..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt +++ /dev/null @@ -1,18 +0,0 @@ -Turris Mox Moxtet GPIO expander via Moxtet bus - -Required properties: - - compatible : Should be "cznic,moxtet-gpio". - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Other properties are required for a Moxtet bus device, please refer to -Documentation/devicetree/bindings/bus/moxtet.txt. - -Example: - - moxtet_sfp: gpio@0 { - compatible = "cznic,moxtet-gpio"; - gpio-controller; - #gpio-cells = <2>; - reg = <0>; - } diff --git a/Documentation/devicetree/bindings/gpio/gpio-palmas.txt b/Documentation/devicetree/bindings/gpio/gpio-palmas.txt deleted file mode 100644 index 08b5b52a3ae05..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-palmas.txt +++ /dev/null @@ -1,27 +0,0 @@ -Palmas GPIO controller bindings - -Required properties: -- compatible: - - "ti,palams-gpio" for palma series of the GPIO controller - - "ti,tps80036-gpio" for Palma series device TPS80036. - - "ti,tps65913-gpio" for palma series device TPS65913. - - "ti,tps65914-gpio" for palma series device TPS65914. -- #gpio-cells : Should be two. - - first cell is the gpio pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller : Marks the device node as a GPIO controller. - -Note: This gpio node will be sub node of palmas node. - -Example: - palmas: tps65913@58 { - ::::::::::: - palmas_gpio: palmas_gpio { - compatible = "ti,palmas-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - ::::::::::: - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml deleted file mode 100644 index 6f73961001b75..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/gpio-pca9570.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: PCA9570 I2C GPO expander - -maintainers: - - Sungbo Eo - -properties: - compatible: - enum: - - dlg,slg7xl45106 - - nxp,pca9570 - - nxp,pca9571 - - reg: - maxItems: 1 - - gpio-controller: true - - '#gpio-cells': - const: 2 - - gpio-line-names: - minItems: 4 - maxItems: 8 - - label: - description: A descriptive name for this device. - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - gpio@24 { - compatible = "nxp,pca9570"; - reg = <0x24>; - gpio-controller; - #gpio-cells = <2>; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml deleted file mode 100644 index 157969bc4c463..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/gpio-tpic2810.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: TPIC2810 GPIO controller - -maintainers: - - Aswath Govindraju - -properties: - compatible: - enum: - - ti,tpic2810 - - reg: - maxItems: 1 - - gpio-controller: true - - "#gpio-cells": - const: 2 - - gpio-line-names: - minItems: 1 - maxItems: 32 - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - #include - - i2c { - #address-cells = <1>; - #size-cells = <0>; - gpio@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - gpio-line-names = "LED A", "LED B", "LED C"; - }; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt deleted file mode 100644 index 92ea9c8f6399e..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ts4800.txt +++ /dev/null @@ -1,20 +0,0 @@ -* TS-4800 FPGA's GPIO controller bindings - -Required properties: -- compatible: Must be "technologic,ts4800-gpio". -- #gpio-cells: Should be two. The first cell is the pin number. -- reg: Physical base address of the controller and length - of memory mapped region. - -Optional property: -- ngpios: See "gpio.txt" - -Example: - -gpio1: gpio { - compatible = "technologic,ts4800-gpio"; - reg = <0x10020 0x6>; - ngpios = <8>; - gpio-controller; - #gpio-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt deleted file mode 100644 index 3f8e71b1ab2a0..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt +++ /dev/null @@ -1,30 +0,0 @@ -* Technologic Systems I2C-FPGA's GPIO controller bindings - -This bindings describes the GPIO controller for Technologic's FPGA core. -TS-4900's FPGA encodes the GPIO state on 3 bits, whereas the TS-7970's FPGA -uses 2 bits: it doesn't use a dedicated input bit. - -Required properties: -- compatible: Should be one of the following - "technologic,ts4900-gpio" - "technologic,ts7970-gpio" -- reg: Physical base address of the controller and length - of memory mapped region. -- #gpio-cells: Should be two. The first cell is the pin number. -- gpio-controller: Marks the device node as a gpio controller. - -Optional property: -- ngpios: Number of GPIOs this controller is instantiated with, - the default is 32. See gpio.txt for more details. - -Example: - -&i2c2 { - gpio8: gpio@28 { - compatible = "technologic,ts4900-gpio"; - reg = <0x28>; - #gpio-cells = <2>; - gpio-controller; - ngpios = <32>; - }; -}; diff --git a/Documentation/devicetree/bindings/gpio/gpio-xgene.txt b/Documentation/devicetree/bindings/gpio/gpio-xgene.txt deleted file mode 100644 index 86dbb05e77584..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-xgene.txt +++ /dev/null @@ -1,22 +0,0 @@ -APM X-Gene SoC GPIO controller bindings - -This is a gpio controller that is part of the flash controller. -This gpio controller controls a total of 48 gpios. - -Required properties: -- compatible: "apm,xgene-gpio" for X-Gene GPIO controller -- reg: Physical base address and size of the controller's registers -- #gpio-cells: Should be two. - - first cell is the pin number - - second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low -- gpio-controller: Marks the device node as a GPIO controller. - -Example: - gpio0: gpio0@1701c000 { - compatible = "apm,xgene-gpio"; - reg = <0x0 0x1701c000 0x0 0x40>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt b/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt deleted file mode 100644 index d58b3958f3eaf..0000000000000 --- a/Documentation/devicetree/bindings/gpio/ibm,ppc4xx-gpio.txt +++ /dev/null @@ -1,24 +0,0 @@ -* IBM/AMCC/APM GPIO Controller for PowerPC 4XX series and compatible SoCs - -All GPIOs are pin-shared with other functions. DCRs control whether a -particular pin that has GPIO capabilities acts as a GPIO or is used for -another purpose. GPIO outputs are separately programmable to emulate -an open-drain driver. - -Required properties: - - compatible: must be "ibm,ppc4xx-gpio" - - reg: address and length of the register set for the device - - #gpio-cells: must be set to 2. The first cell is the pin number - and the second cell is used to specify the gpio polarity: - 0 = active high - 1 = active low - - gpio-controller: marks the device node as a gpio controller. - -Example: - -GPIO0: gpio@ef600b00 { - compatible = "ibm,ppc4xx-gpio"; - reg = <0xef600b00 0x00000048>; - #gpio-cells = <2>; - gpio-controller; -}; diff --git a/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml b/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml deleted file mode 100644 index 1a472c05697c1..0000000000000 --- a/Documentation/devicetree/bindings/gpio/loongson,ls1x-gpio.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/loongson,ls1x-gpio.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Loongson-1 GPIO controller - -maintainers: - - Keguang Zhang - -properties: - compatible: - const: loongson,ls1x-gpio - - reg: - maxItems: 1 - - gpio-controller: true - - "#gpio-cells": - const: 2 - - ngpios: - minimum: 1 - maximum: 32 - -required: - - compatible - - reg - - gpio-controller - - "#gpio-cells" - - ngpios - -additionalProperties: false - -examples: - - | - gpio0: gpio@1fd010c0 { - compatible = "loongson,ls1x-gpio"; - reg = <0x1fd010c0 0x4>; - - gpio-controller; - #gpio-cells = <2>; - - ngpios = <32>; - }; - -... diff --git a/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt b/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt deleted file mode 100644 index df63da46309cb..0000000000000 --- a/Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt +++ /dev/null @@ -1,26 +0,0 @@ -Nintendo Wii (Hollywood) GPIO controller - -Required properties: -- compatible: "nintendo,hollywood-gpio" -- reg: Physical base address and length of the controller's registers. -- gpio-controller: Marks the device node as a GPIO controller. -- #gpio-cells: Should be <2>. The first cell is the pin number and the - second cell is used to specify optional parameters: - - bit 0 specifies polarity (0 for normal, 1 for inverted). - -Optional properties: -- ngpios: see Documentation/devicetree/bindings/gpio/gpio.txt -- interrupt-controller: Marks the device node as an interrupt controller. -- #interrupt-cells: Should be two. -- interrupts: Interrupt specifier for the controller's Broadway (PowerPC) - interrupt. - -Example: - - GPIO: gpio@d8000c0 { - #gpio-cells = <2>; - compatible = "nintendo,hollywood-gpio"; - reg = <0x0d8000c0 0x40>; - gpio-controller; - ngpios = <24>; - } diff --git a/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml b/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml deleted file mode 100644 index d8cce73ea0ae7..0000000000000 --- a/Documentation/devicetree/bindings/gpio/rockchip,rk3328-grf-gpio.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/rockchip,rk3328-grf-gpio.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Rockchip RK3328 General Register Files GPIO controller - -description: - The Rockchip RK3328 General Register File (GRF) outputs only the - GPIO_MUTE pin, originally for codec mute control, but it can also be used - for general purpose. It is manipulated by the GRF_SOC_CON10 register. - If needed in the future support for the HDMI pins can also be added. - The GPIO node should be declared as the child of the GRF node. - - The GPIO_MUTE pin is referred to in the format - - <&grf_gpio 0 GPIO_ACTIVE_LOW> - - The first cell is the pin number and - the second cell is used to specify the GPIO polarity - 0 = Active high - 1 = Active low - -maintainers: - - Heiko Stuebner - -properties: - compatible: - const: rockchip,rk3328-grf-gpio - - gpio-controller: true - - "#gpio-cells": - const: 2 - -required: - - compatible - - gpio-controller - - "#gpio-cells" - -additionalProperties: false - -examples: - - | - grf_gpio: gpio { - compatible = "rockchip,rk3328-grf-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt b/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt deleted file mode 100644 index 1b30812b015b6..0000000000000 --- a/Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt +++ /dev/null @@ -1,21 +0,0 @@ -Synopsys GPIO via CREG (Control REGisters) driver - -Required properties: -- compatible : "snps,creg-gpio-hsdk" or "snps,creg-gpio-axs10x". -- reg : Exactly one register range with length 0x4. -- #gpio-cells : Since the generic GPIO binding is used, the - amount of cells must be specified as 2. The first cell is the - pin number, the second cell is used to specify optional parameters: - See "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt. -- gpio-controller : Marks the device node as a GPIO controller. -- ngpios: Number of GPIO pins. - -Example: - -gpio: gpio@f00014b0 { - compatible = "snps,creg-gpio-hsdk"; - reg = <0xf00014b0 0x4>; - gpio-controller; - #gpio-cells = <2>; - ngpios = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/ti,7416374.yaml b/Documentation/devicetree/bindings/gpio/ti,7416374.yaml deleted file mode 100644 index 33472f0911011..0000000000000 --- a/Documentation/devicetree/bindings/gpio/ti,7416374.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/gpio/ti,7416374.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: TI 74XX MMIO GPIO - -maintainers: - - Frank Li - -properties: - compatible: - enum: - - ti,741g125 # for 741G125 (1-bit Input), - - ti,741g174 # for 741G74 (1-bit Output), - - ti,742g125 # for 742G125 (2-bit Input), - - ti,7474 # for 7474 (2-bit Output), - - ti,74125 # for 74125 (4-bit Input), - - ti,74175 # for 74175 (4-bit Output), - - ti,74365 # for 74365 (6-bit Input), - - ti,74174 # for 74174 (6-bit Output), - - ti,74244 # for 74244 (8-bit Input), - - ti,74273 # for 74273 (8-bit Output), - - ti,741624 # for 741624 (16-bit Input), - - ti,7416374 # for 7416374 (16-bit Output). - - reg: - maxItems: 1 - - gpio-controller: true - - '#gpio-cells': - const: 2 - description: | - The first cell is the pin number and - the second cell is used to specify the GPIO polarity: - 0 = Active High, - 1 = Active Low. - -required: - - compatible - - reg - - gpio-controller - - '#gpio-cells' - -additionalProperties: false - -examples: - - | - gpio@30008004 { - compatible = "ti,74174"; - reg = <0x30008004 0x1>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml new file mode 100644 index 0000000000000..0299d4a25086a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trivial-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Trivial 2-cell GPIO controllers + +maintainers: + - Bartosz Golaszewski + +properties: + compatible: + oneOf: + - items: + - enum: + - cirrus,ep7312-gpio + - const: cirrus,ep7209-gpio + - enum: + - apm,xgene-gpio + - cirrus,ep7209-gpio + - cznic,moxtet-gpio + - dlg,slg7xl45106 + - fcs,fxl6408 + - gateworks,pld-gpio + - ibm,ppc4xx-gpio + - loongson,ls1x-gpio + - maxim,max77620 + - nintendo,hollywood-gpio + - nxp,pca9570 + - nxp,pca9571 + - rockchip,rk3328-grf-gpio + - snps,creg-gpio-hsdk + - technologic,ts4800-gpio + - technologic,ts4900-gpio + - technologic,ts7970-gpio + - ti,741g125 # for 741G125 (1-bit Input), + - ti,741g174 # for 741G74 (1-bit Output), + - ti,742g125 # for 742G125 (2-bit Input), + - ti,7474 # for 7474 (2-bit Output), + - ti,74125 # for 74125 (4-bit Input), + - ti,74175 # for 74175 (4-bit Output), + - ti,74365 # for 74365 (6-bit Input), + - ti,74174 # for 74174 (6-bit Output), + - ti,74244 # for 74244 (8-bit Input), + - ti,74273 # for 74273 (8-bit Output), + - ti,741624 # for 741624 (16-bit Input), + - ti,7416374 # for 7416374 (16-bit Output). + - ti,lp3943-gpio + - ti,palmas-gpio + - ti,tpic2810 + - ti,tps80036-gpio + - ti,tps65913-gpio + - ti,tps65914-gpio + + reg: + maxItems: 1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + gpio-line-names: true + + ngpios: true + + # Don't add more properties + +patternProperties: + "^(hog-[0-9]+|.+-hog(-[0-9]+)?)$": + type: object + required: + - gpio-hog + +required: + - compatible + - '#gpio-cells' + - gpio-controller + +allOf: + - if: + properties: + compatible: + contains: + enum: + - maxim,max77620 + - rockchip,rk3328-grf-gpio + - ti,lp3943-gpio + - ti,palmas-gpio + - ti,tps80036-gpio + - ti,tps65913-gpio + - ti,tps65914-gpio + then: + properties: + reg: false + else: + required: + - reg + +additionalProperties: false + +examples: + - | + gpio@1701c000 { + compatible = "apm,xgene-gpio"; + reg = <0x1701c000 0x40>; + gpio-controller; + #gpio-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/mfd/lp3943.txt b/Documentation/devicetree/bindings/mfd/lp3943.txt index e8591d6b11b49..ca5324ed0df4c 100644 --- a/Documentation/devicetree/bindings/mfd/lp3943.txt +++ b/Documentation/devicetree/bindings/mfd/lp3943.txt @@ -7,7 +7,7 @@ Required properties: LP3943 consists of two sub-devices, lp3943-gpio and lp3943-pwm. For the LP3943 GPIO properties please refer to: -Documentation/devicetree/bindings/gpio/gpio-lp3943.txt +Documentation/devicetree/bindings/gpio/trivial-gpio.yaml For the LP3943 PWM properties please refer to: Documentation/devicetree/bindings/pwm/pwm-lp3943.txt diff --git a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt index 6f69a9dfe198c..df060a0d7d4a2 100644 --- a/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt +++ b/Documentation/devicetree/bindings/powerpc/nintendo/wii.txt @@ -139,10 +139,6 @@ Nintendo Wii device tree - interrupt-controller - interrupts : should contain the cascade interrupt of the "flipper" pic -1.l) The General Purpose I/O (GPIO) controller node - - see Documentation/devicetree/bindings/gpio/nintendo,hollywood-gpio.txt - 1.m) The control node Represents the control interface used to setup several miscellaneous diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index ccdcc889ba8ef..1ab0b092e2a5f 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -179,10 +179,12 @@ allOf: properties: gpio: type: object + properties: + compatible: + contains: + const: rockchip,rk3328-grf-gpio - $ref: /schemas/gpio/rockchip,rk3328-grf-gpio.yaml# - - unevaluatedProperties: false + additionalProperties: true power-controller: type: object diff --git a/MAINTAINERS b/MAINTAINERS index 8529e7c09d584..2e9eb4a99a2a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2546,7 +2546,6 @@ F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm F: Documentation/devicetree/bindings/bus/moxtet.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml -F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt F: Documentation/devicetree/bindings/interrupt-controller/marvell,mpic.yaml F: Documentation/devicetree/bindings/leds/cznic,turris-omnia-leds.yaml F: Documentation/devicetree/bindings/watchdog/armada-37xx-wdt.txt @@ -23996,7 +23995,6 @@ F: drivers/reset/reset-axs10x.c SYNOPSYS CREG GPIO DRIVER M: Eugeniy Paltsev S: Maintained -F: Documentation/devicetree/bindings/gpio/snps,creg-gpio.txt F: drivers/gpio/gpio-creg-snps.c SYNOPSYS DESIGNWARE 8250 UART DRIVER @@ -27277,7 +27275,6 @@ XRA1403 GPIO EXPANDER M: Nandor Han L: linux-gpio@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/gpio/gpio-xra1403.txt F: drivers/gpio/gpio-xra1403.c XTENSA XTFPGA PLATFORM SUPPORT -- GitLab From e2337e64fce36a8ed6cb44771482e9dabb24eea9 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:29:40 -0500 Subject: [PATCH 791/868] dt-bindings: gpio: fsl,qoriq-gpio: Add missing mpc8xxx compatibles The fsl,mpc8349-gpio, fsl,mpc8572-gpio, and fsl,mpc8610-gpio compatibles are already documented in fsl,qoriq-gpio.yaml. Add the additional compatibles that use fsl,mpc8349-gpio as a fallback. With that, the 8xxx_gpio.txt binding document is redundant and can be removed. Signed-off-by: Rob Herring (Arm) Acked-by: Ioana Ciornei Link: https://lore.kernel.org/r/20250714202941.3013390-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/8xxx_gpio.txt | 72 ------------------- .../bindings/gpio/fsl,qoriq-gpio.yaml | 7 ++ 2 files changed, 7 insertions(+), 72 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/8xxx_gpio.txt diff --git a/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt b/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt deleted file mode 100644 index 973362eb3f1e7..0000000000000 --- a/Documentation/devicetree/bindings/gpio/8xxx_gpio.txt +++ /dev/null @@ -1,72 +0,0 @@ -GPIO controllers on MPC8xxx SoCs - -This is for the non-QE/CPM/GUTs GPIO controllers as found on -8349, 8572, 8610 and compatible. - -Every GPIO controller node must have #gpio-cells property defined, -this information will be used to translate gpio-specifiers. -See bindings/gpio/gpio.txt for details of how to specify GPIO -information for devices. - -The GPIO module usually is connected to the SoC's internal interrupt -controller, see bindings/interrupt-controller/interrupts.txt (the -interrupt client nodes section) for details how to specify this GPIO -module's interrupt. - -The GPIO module may serve as another interrupt controller (cascaded to -the SoC's internal interrupt controller). See the interrupt controller -nodes section in bindings/interrupt-controller/interrupts.txt for -details. - -Required properties: -- compatible: "fsl,-gpio" followed by "fsl,mpc8349-gpio" - for 83xx, "fsl,mpc8572-gpio" for 85xx, or - "fsl,mpc8610-gpio" for 86xx. -- #gpio-cells: Should be two. The first cell is the pin number - and the second cell is used to specify optional - parameters (currently unused). -- interrupts: Interrupt mapping for GPIO IRQ. -- gpio-controller: Marks the port as GPIO controller. - -Optional properties: -- interrupt-controller: Empty boolean property which marks the GPIO - module as an IRQ controller. -- #interrupt-cells: Should be two. Defines the number of integer - cells required to specify an interrupt within - this interrupt controller. The first cell - defines the pin number, the second cell - defines additional flags (trigger type, - trigger polarity). Note that the available - set of trigger conditions supported by the - GPIO module depends on the actual SoC. - -Example of gpio-controller nodes for a MPC8347 SoC: - - gpio1: gpio-controller@c00 { - #gpio-cells = <2>; - compatible = "fsl,mpc8347-gpio", "fsl,mpc8349-gpio"; - reg = <0xc00 0x100>; - interrupt-parent = <&ipic>; - interrupts = <74 0x8>; - gpio-controller; - interrupt-controller; - #interrupt-cells = <2>; - }; - - gpio2: gpio-controller@d00 { - #gpio-cells = <2>; - compatible = "fsl,mpc8347-gpio", "fsl,mpc8349-gpio"; - reg = <0xd00 0x100>; - interrupt-parent = <&ipic>; - interrupts = <75 0x8>; - gpio-controller; - }; - -Example of a peripheral using the GPIO module as an IRQ controller: - - funkyfpga@0 { - compatible = "funky-fpga"; - ... - interrupt-parent = <&gpio1>; - interrupts = <4 3>; - }; diff --git a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml index f1b60ab3f356b..4cb2a6b9fabfb 100644 --- a/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl,qoriq-gpio.yaml @@ -29,6 +29,13 @@ properties: - fsl,ls1088a-gpio - fsl,ls2080a-gpio - const: fsl,qoriq-gpio + - items: + - enum: + - fsl,mpc8308-gpio + - fsl,mpc8377-gpio + - fsl,mpc8378-gpio + - fsl,mpc8379-gpio + - const: fsl,mpc8349-gpio reg: maxItems: 1 -- GitLab From 07e858e7e1932c97509a874cf753b09ce3f167ca Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:41 -0500 Subject: [PATCH 792/868] dt-bindings: gpio: Convert maxim,max3191x to DT schema Convert the Maxim MAX3191x and similar GPIO binding to DT schema format. It's a straight forward conversion. Signed-off-by: Rob Herring (Arm) Reviewed-by: Lukas Wunner Link: https://lore.kernel.org/r/20250714202843.3011698-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/gpio-max3191x.txt | 59 ---------- .../bindings/gpio/maxim,max31910.yaml | 104 ++++++++++++++++++ 2 files changed, 104 insertions(+), 59 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-max3191x.txt create mode 100644 Documentation/devicetree/bindings/gpio/maxim,max31910.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt b/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt deleted file mode 100644 index b3a6444b8f455..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-max3191x.txt +++ /dev/null @@ -1,59 +0,0 @@ -GPIO driver for Maxim MAX3191x industrial serializer - -Required properties: - - compatible: Must be one of: - "maxim,max31910" - "maxim,max31911" - "maxim,max31912" - "maxim,max31913" - "maxim,max31953" - "maxim,max31963" - - reg: Chip select number. - - gpio-controller: Marks the device node as a GPIO controller. - - #gpio-cells: Should be two. For consumer use see gpio.txt. - -Optional properties: - - #daisy-chained-devices: - Number of chips in the daisy-chain (default is 1). - - maxim,modesel-gpios: GPIO pins to configure modesel of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - (if each chip is driven by a separate pin) or 1 - (if all chips are wired to the same pin). - - maxim,fault-gpios: GPIO pins to read fault of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - or 1. - - maxim,db0-gpios: GPIO pins to configure debounce of each chip. - The number of GPIOs must equal "#daisy-chained-devices" - or 1. - - maxim,db1-gpios: GPIO pins to configure debounce of each chip. - The number of GPIOs must equal "maxim,db0-gpios". - - maxim,modesel-8bit: Boolean whether the modesel pin of the chips is - pulled high (8-bit mode). Use this if the modesel pin - is hardwired and consequently "maxim,modesel-gpios" - cannot be specified. By default if neither this nor - "maxim,modesel-gpios" is given, the driver assumes - that modesel is pulled low (16-bit mode). - - maxim,ignore-undervoltage: - Boolean whether to ignore undervoltage alarms signaled - by the "maxim,fault-gpios" or by the status byte - (in 16-bit mode). Use this if the chips are powered - through 5VOUT instead of VCC24V, in which case they - will constantly signal undervoltage. - -For other required and optional properties of SPI slave nodes please refer to -../spi/spi-bus.txt. - -Example: - gpio@0 { - compatible = "maxim,max31913"; - reg = <0>; - gpio-controller; - #gpio-cells = <2>; - - maxim,modesel-gpios = <&gpio2 23>; - maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>; - maxim,db0-gpios = <&gpio2 25>; - maxim,db1-gpios = <&gpio2 26>; - - spi-max-frequency = <25000000>; - }; diff --git a/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml b/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml new file mode 100644 index 0000000000000..82a190a715f94 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/maxim,max31910.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/maxim,max31910.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX3191x GPIO serializer + +maintainers: + - Lukas Wunner + +properties: + compatible: + enum: + - maxim,max31910 + - maxim,max31911 + - maxim,max31912 + - maxim,max31913 + - maxim,max31953 + - maxim,max31963 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + '#daisy-chained-devices': + description: Number of chips in the daisy-chain. + default: 1 + + maxim,modesel-gpios: + description: + GPIO pins to configure modesel of each chip. The number of GPIOs must + equal "#daisy-chained-devices" (if each chip is driven by a separate pin) + or 1 (if all chips are wired to the same pin). + + maxim,fault-gpios: + description: + GPIO pins to read fault of each chip. The number of GPIOs must equal + "#daisy-chained-devices" or 1. + + maxim,db0-gpios: + description: + GPIO pins to configure debounce of each chip. The number of GPIOs must + equal "#daisy-chained-devices" or 1. + + maxim,db1-gpios: + description: + GPIO pins to configure debounce of each chip. The number of GPIOs must + equal "maxim,db0-gpios". + + maxim,modesel-8bit: + description: + Boolean whether the modesel pin of the chips is pulled high (8-bit mode). + Use this if the modesel pin is hardwired and consequently + "maxim,modesel-gpios" cannot be specified. By default if neither this nor + "maxim,modesel-gpios" is given, the driver assumes that modesel is pulled + low (16-bit mode). + type: boolean + + maxim,ignore-undervoltage: + description: + Boolean whether to ignore undervoltage alarms signaled by the + "maxim,fault-gpios" or by the status byte (in 16-bit mode). Use this if + the chips are powered through 5VOUT instead of VCC24V, in which case they + will constantly signal undervoltage. + type: boolean + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + gpio@0 { + compatible = "maxim,max31913"; + reg = <0>; + gpio-controller; + #gpio-cells = <2>; + + maxim,modesel-gpios = <&gpio2 23>; + maxim,fault-gpios = <&gpio2 24 GPIO_ACTIVE_LOW>; + maxim,db0-gpios = <&gpio2 25>; + maxim,db1-gpios = <&gpio2 26>; + + spi-max-frequency = <25000000>; + }; + }; -- GitLab From ae455b249449ad6306500324aa76f03b46295599 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 14 Jul 2025 15:28:58 -0500 Subject: [PATCH 793/868] dt-bindings: gpio: Convert qca,ar7100-gpio to DT schema Convert the Qualcomm Atheros AR7xxx/AR9xxx GPIO binding to DT schema format. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250714202859.3012173-1-robh@kernel.org Signed-off-by: Bartosz Golaszewski --- .../devicetree/bindings/gpio/gpio-ath79.txt | 37 ------------ .../bindings/gpio/qca,ar7100-gpio.yaml | 60 +++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 61 insertions(+), 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/gpio-ath79.txt create mode 100644 Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-ath79.txt b/Documentation/devicetree/bindings/gpio/gpio-ath79.txt deleted file mode 100644 index cf71f3ec969d5..0000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-ath79.txt +++ /dev/null @@ -1,37 +0,0 @@ -Binding for Qualcomm Atheros AR7xxx/AR9xxx GPIO controller - -Required properties: -- compatible: has to be "qca,-gpio" and one of the following - fallbacks: - - "qca,ar7100-gpio" - - "qca,ar9340-gpio" -- reg: Base address and size of the controllers memory area -- gpio-controller : Marks the device node as a GPIO controller. -- #gpio-cells : Should be two. The first cell is the pin number and the - second cell is used to specify optional parameters. -- ngpios: Should be set to the number of GPIOs available on the SoC. - -Optional properties: -- interrupts: Interrupt specifier for the controllers interrupt. -- interrupt-controller : Identifies the node as an interrupt controller -- #interrupt-cells : Specifies the number of cells needed to encode interrupt - source, should be 2 - -Please refer to interrupts.txt in this directory for details of the common -Interrupt Controllers bindings used by client devices. - -Example: - - gpio@18040000 { - compatible = "qca,ar9132-gpio", "qca,ar7100-gpio"; - reg = <0x18040000 0x30>; - interrupts = <2>; - - ngpios = <22>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml b/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml new file mode 100644 index 0000000000000..519c4c2158f7c --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/qca,ar7100-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Atheros AR7xxx/AR9xxx GPIO controller + +maintainers: + - Alban Bedel + +properties: + compatible: + oneOf: + - items: + - const: qca,ar9132-gpio + - const: qca,ar7100-gpio + - enum: + - qca,ar7100-gpio + - qca,ar9340-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + const: 2 + + ngpios: true + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - ngpios + +additionalProperties: false + +examples: + - | + gpio@18040000 { + compatible = "qca,ar9132-gpio", "qca,ar7100-gpio"; + reg = <0x18040000 0x30>; + interrupts = <2>; + ngpios = <22>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 2e9eb4a99a2a1..971ee194ee83f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3815,7 +3815,7 @@ M: Alban Bedel S: Maintained W: https://github.com/AlbanBedel/linux T: git git://github.com/AlbanBedel/linux -F: Documentation/devicetree/bindings/gpio/gpio-ath79.txt +F: Documentation/devicetree/bindings/gpio/qca,ar7100-gpio.yaml F: drivers/gpio/gpio-ath79.c ATHEROS 71XX/9XXX USB PHY DRIVER -- GitLab From d511206dc7443120637efd9cfa3ab06a26da33dd Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Fri, 18 Jul 2025 16:11:36 +0200 Subject: [PATCH 794/868] regulator: core: repeat voltage setting request for stepped regulators The regulator_set_voltage() function may exhibit unexpected behavior if the target regulator has a maximum voltage step constraint. With such a constraint, the regulator core may clamp the requested voltage to a lesser value, to ensure that the voltage delta stays under the specified limit. This means that the resulting regulator voltage depends on the current voltage, as well as the requested range, which invalidates the assumption that a repeated request for a specific voltage range will amount to a noop. Considering the case of a regulator with a maximum voltage step constraint of 1V: initial voltage: 2.5V consumer requests 4V expected result: 3.5V resulting voltage: 3.5V consumer requests 4V again expected result: 4V actual result: 3.5V Correct this by repeating attempts to balance the regulator voltage until the result converges. Signed-off-by: Romain Gantois Link: https://patch.msgid.link/20250718-regulator-stepping-v2-1-e28c9ac5d54a@bootlin.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 43 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cbd6d53ebfb5e..8ed9b96518cf5 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3797,6 +3797,16 @@ static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev, return 0; } +static int regulator_get_voltage_delta(struct regulator_dev *rdev, int uV) +{ + int current_uV = regulator_get_voltage_rdev(rdev); + + if (current_uV < 0) + return current_uV; + + return abs(current_uV - uV); +} + static int regulator_set_voltage_unlocked(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) @@ -3804,8 +3814,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; struct regulator_voltage *voltage = ®ulator->voltage[state]; int ret = 0; + int current_uV, delta, new_delta; int old_min_uV, old_max_uV; - int current_uV; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -3852,6 +3862,37 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, voltage->max_uV = old_max_uV; } + if (rdev->constraints->max_uV_step > 0) { + /* For regulators with a maximum voltage step, reaching the desired + * voltage might take a few retries. + */ + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + delta = ret; + + while (delta > 0) { + ret = regulator_balance_voltage(rdev, state); + if (ret < 0) + goto out; + + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + new_delta = ret; + + /* check that voltage is converging quickly enough */ + if (new_delta - delta > rdev->constraints->max_uV_step) { + ret = -EWOULDBLOCK; + goto out; + } + + delta = new_delta; + } + } + out: return ret; } -- GitLab From 5607f5ed3c5f30f41e72ce09c8e616af0fc0d474 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Jul 2025 16:22:15 -0500 Subject: [PATCH 795/868] gpio: sysfs: Fix an end of loop test in gpiod_unexport() The test for "if (!desc_data)" does not work correctly because the list iterator in a list_for_each_entry() loop is always non-NULL. If we don't exit via a break, then it points to invalid memory. Instead, use a tmp variable for the list iterator and only set the "desc_data" when we have found a match. Fixes: 1cd53df733c2 ("gpio: sysfs: don't look up exported lines as class devices") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/747545bf-05f0-4f89-ba77-cb96bf9041f1@sabinyo.mountain Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index f31adc56bef1e..b64106f1cb7b9 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -927,7 +927,7 @@ EXPORT_SYMBOL_GPL(gpiod_export_link); */ void gpiod_unexport(struct gpio_desc *desc) { - struct gpiod_data *desc_data = NULL; + struct gpiod_data *tmp, *desc_data = NULL; struct gpiodev_data *gdev_data; struct gpio_device *gdev; @@ -945,9 +945,12 @@ void gpiod_unexport(struct gpio_desc *desc) if (!gdev_data) return; - list_for_each_entry(desc_data, &gdev_data->exported_lines, list) - if (gpiod_is_equal(desc, desc_data->desc)) + list_for_each_entry(tmp, &gdev_data->exported_lines, list) { + if (gpiod_is_equal(desc, tmp->desc)) { + desc_data = tmp; break; + } + } if (!desc_data) return; -- GitLab From 62b2e01966dd7e8fd588d09e9e2fb99b7588d97a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 19 Jul 2025 14:38:41 +0200 Subject: [PATCH 796/868] ACPI/PNP: Use my kernel.org address in MAINTAINERS and ABI docs For the sake of consistency, use my kernel.org address in all Contact records in sysfs-bus-acpi and in the MAINTAINERS records related to ACPI and PNP. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2796086.mvXUDI8C0e@rjwysocki.net --- Documentation/ABI/testing/sysfs-bus-acpi | 18 +++++++++--------- MAINTAINERS | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-acpi b/Documentation/ABI/testing/sysfs-bus-acpi index 58abacf59b2ab..6f2b907a80132 100644 --- a/Documentation/ABI/testing/sysfs-bus-acpi +++ b/Documentation/ABI/testing/sysfs-bus-acpi @@ -1,6 +1,6 @@ What: /sys/bus/acpi/devices/.../path Date: December 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the full path of ACPI namespace object associated with the device object. For example, @@ -12,7 +12,7 @@ Description: What: /sys/bus/acpi/devices/.../modalias Date: July 2007 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the PNP IDs of the device object. That is acpi:HHHHHHHH:[CCCCCCC:]. Where each HHHHHHHH or @@ -20,7 +20,7 @@ Description: What: /sys/bus/acpi/devices/.../hid Date: April 2005 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute indicates the hardware ID (_HID) of the device object. For example, PNP0103. @@ -29,14 +29,14 @@ Description: What: /sys/bus/acpi/devices/.../description Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _STR control method, if present. What: /sys/bus/acpi/devices/.../adr Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _ADR control method, which is present for ACPI device @@ -45,14 +45,14 @@ Description: What: /sys/bus/acpi/devices/.../uid Date: October 2012 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: This attribute contains the output of the device object's _UID control method, if present. What: /sys/bus/acpi/devices/.../eject Date: December 2006 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: Writing 1 to this attribute will trigger hot removal of this device object. This file exists for every device @@ -60,7 +60,7 @@ Description: What: /sys/bus/acpi/devices/.../status Date: Jan, 2014 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: (RO) Returns the ACPI device status: enabled, disabled or functioning or present, if the method _STA is present. @@ -90,7 +90,7 @@ Description: What: /sys/bus/acpi/devices/.../hrv Date: Apr, 2016 -Contact: Rafael J. Wysocki +Contact: Rafael J. Wysocki Description: (RO) Allows users to read the hardware version of non-PCI hardware, if the _HRV control method is present. It is mostly diff --git a/MAINTAINERS b/MAINTAINERS index 10850512c1186..a0ef3e7c33c7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -314,8 +314,8 @@ L: linux-acpi@vger.kernel.org F: drivers/acpi/apei/ ACPI COMPONENT ARCHITECTURE (ACPICA) +M: "Rafael J. Wysocki" M: Robert Moore -M: "Rafael J. Wysocki" L: linux-acpi@vger.kernel.org L: acpica-devel@lists.linux.dev S: Supported @@ -19769,7 +19769,7 @@ F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.yaml F: drivers/iio/magnetometer/rm3100* PNP SUPPORT -M: "Rafael J. Wysocki" +M: "Rafael J. Wysocki" L: linux-acpi@vger.kernel.org S: Maintained F: drivers/pnp/ -- GitLab From aad2f87cbcab56b322109d26d7b11842a09df91f Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sat, 19 Jul 2025 08:33:52 +0200 Subject: [PATCH 797/868] dt-bindings: trivial-devices: Document ABB sensors Add documentation for spi based ABB sensors, which are currently operated from userspace. Signed-off-by: Heiko Schocher Link: https://patch.msgid.link/20250719063355.73111-2-hs@denx.de Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 27930708ccd58..25260c2b81df2 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -30,6 +30,8 @@ properties: items: # Entries are sorted alphanumerically by the compatible - enum: + # ABB register based spi sensors + - abb,spi-sensor # Acbel fsg032 power supply - acbel,fsg032 # SMBus/I2C Digital Temperature Sensor in 6-Pin SOT with SMBus Alert and Over Temperature Pin -- GitLab From d60f7cab7c04944a79af16caa43c141e780a59c6 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sat, 19 Jul 2025 08:33:53 +0200 Subject: [PATCH 798/868] spi: spidev: Add an entry for the ABB spi sensors This sensors are currently controlled from userspace, ideally we will add full drivers in the future. Signed-off-by: Heiko Schocher Link: https://patch.msgid.link/20250719063355.73111-3-hs@denx.de Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 6108959c28d9c..5300c942a2a44 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -703,6 +703,7 @@ static const struct class spidev_class = { * spidev_dt_ids array below. Both arrays are kept in the same ordering. */ static const struct spi_device_id spidev_spi_ids[] = { + { .name = /* abb */ "spi-sensor" }, { .name = /* cisco */ "spi-petra" }, { .name = /* dh */ "dhcom-board" }, { .name = /* elgin */ "jg10309-01" }, @@ -735,6 +736,7 @@ static int spidev_of_check(struct device *dev) } static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "abb,spi-sensor", .data = &spidev_of_check }, { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, { .compatible = "dh,dhcom-board", .data = &spidev_of_check }, { .compatible = "elgin,jg10309-01", .data = &spidev_of_check }, -- GitLab From d33bd88ac0ebb49e7f7c8f29a8c7ee9eae85d765 Mon Sep 17 00:00:00 2001 From: Jiayi Li Date: Mon, 21 Jul 2025 11:26:06 +0800 Subject: [PATCH 799/868] ACPI: processor: perflib: Fix initial _PPC limit application If the BIOS sets a _PPC frequency limit upfront, it will fail to take effect due to a call ordering issue. Namely, freq_qos_update_request() is called before freq_qos_add_request() for the given request causing the constraint update to be ignored. The call sequence in question is as follows: cpufreq_policy_online() acpi_cpufreq_cpu_init() acpi_processor_register_performance() acpi_processor_get_performance_info() acpi_processor_get_platform_limit() freq_qos_update_request(&perflib_req) <- inactive QoS request blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY) acpi_processor_notifier() acpi_processor_ppc_init() freq_qos_add_request(&perflib_req) <- QoS request activation Address this by adding an acpi_processor_get_platform_limit() call to acpi_processor_ppc_init(), after the perflib_req activation via freq_qos_add_request(), which causes the initial _PPC limit to be picked up as appropriate. However, also ensure that the _PPC limit will not be picked up in the cases when the cpufreq driver does not call acpi_processor_register_performance() by adding a pr->performance check to the related_cpus loop in acpi_processor_ppc_init(). Fixes: d15ce412737a ("ACPI: cpufreq: Switch to QoS requests instead of cpufreq notifier") Signed-off-by: Jiayi Li Link: https://patch.msgid.link/20250721032606.3459369-1-lijiayi@kylinos.cn [ rjw: Consolidate pr-related checks in acpi_processor_ppc_init() ] [ rjw: Subject and changelog adjustments ] Cc: 5.4+ # 5.4+: 2d8b39a62a5d ACPI: processor: Avoid NULL pointer dereferences at init time Cc: 5.4+ # 5.4+: 3000ce3c52f8 cpufreq: Use per-policy frequency QoS Cc: 5.4+ # 5.4+: a1bb46c36ce3 ACPI: processor: Add QoS requests for all CPUs Cc: 5.4+ # 5.4+ Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_perflib.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 64b8d1e195943..755003bf3a458 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -173,11 +173,14 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy) { unsigned int cpu; + if (ignore_ppc == 1) + return; + for_each_cpu(cpu, policy->related_cpus) { struct acpi_processor *pr = per_cpu(processors, cpu); int ret; - if (!pr) + if (!pr || !pr->performance) continue; /* @@ -193,6 +196,11 @@ void acpi_processor_ppc_init(struct cpufreq_policy *policy) if (ret < 0) pr_err("Failed to add freq constraint for CPU%d (%d)\n", cpu, ret); + + ret = acpi_processor_get_platform_limit(pr); + if (ret) + pr_err("Failed to update freq constraint for CPU%d (%d)\n", + cpu, ret); } } -- GitLab From 5a9fffd8a533bfb2688ec69dd6d1b6e53ef1177a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 14 Jul 2025 10:15:43 +0200 Subject: [PATCH 800/868] platform/x86/intel/pmt: fix build dependency for kunit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When INTEL_PMT_TELEMETRY is in a loadable module, the discovery test cannot be built-in: x86_64-linux-ld: drivers/platform/x86/intel/pmt/discovery-kunit.o: in function `test_intel_pmt_get_regions_by_feature': discovery-kunit.c:(.text+0x29d): undefined reference to `intel_pmt_get_regions_by_feature' x86_64-linux-ld: discovery-kunit.c:(.text+0x2c3): undefined reference to `intel_pmt_put_feature_group' Add a Kconfig dependency to prevent this. Fixes: b9707d46a959 ("platform/x86/intel/pmt: KUNIT test for PMT Enhanced Discovery API") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250714081559.4056777-1-arnd@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index 785c206e1beb8..7363446b77736 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -55,6 +55,7 @@ config INTEL_PMT_DISCOVERY config INTEL_PMT_KUNIT_TEST tristate "KUnit tests for Intel PMT driver" depends on INTEL_PMT_DISCOVERY + depends on INTEL_PMT_TELEMETRY || !INTEL_PMT_TELEMETRY depends on KUNIT help Enable this option to compile and run a suite of KUnit tests for the Intel -- GitLab From 0156c22fb0ca869381fe0520731da79f70389652 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Mon, 21 Jul 2025 14:54:05 +0100 Subject: [PATCH 801/868] ALSA: hda/realtek: Add support for ASUS Commercial laptops using CS35L41 HDA Add support for ASUS PM3406CKA and PM3606CKA. Laptops use 2 CS35L41 Amps with HDA, using Internal boost, with I2C Signed-off-by: Stefan Binding Link: https://patch.msgid.link/20250721135406.366912-1-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index dc3ee1256c2b9..88fc835c70880 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6682,6 +6682,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { 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), + SND_PCI_QUIRK(0x1043, 0x1454, "ASUS PM3406CKA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -6776,6 +6777,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), -- GitLab From 94fd4423036fc336d5211f112dc62a03b28ebba0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 21 Jul 2025 14:50:16 +0100 Subject: [PATCH 802/868] ACPI: processor: throttling: Remove space before newline There is a extraneous space before a newline in a pr_warn message. Remove it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250721135016.2500117-1-colin.i.king@gmail.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_throttling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index d1541a386fbc8..f9c2bc1d4a3ac 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -235,7 +235,7 @@ static int acpi_processor_throttling_notifier(unsigned long event, void *data) if (pr->throttling_platform_limit > target_state) target_state = pr->throttling_platform_limit; if (target_state >= p_throttling->state_count) { - pr_warn("Exceed the limit of T-state \n"); + pr_warn("Exceed the limit of T-state\n"); target_state = p_throttling->state_count - 1; } p_tstate->target_state = target_state; -- GitLab From 17882721dcb49323eaa9728d7eaa2ae826c876f7 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 21 Jul 2025 19:23:46 +0800 Subject: [PATCH 803/868] ASoC: SDCA: add route by the number of input pins in MU entity This patch removed the code where num_sources should be the same as cn_list. For better resilience, it would be preferable to explicitly add the route mapping the input pins to this MU entity. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250721112346.388542-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_asoc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index 1482869a7ad5a..c493ec530cc5c 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -594,7 +594,6 @@ static int entity_parse_mu(struct device *dev, { struct sdca_control *control; struct snd_kcontrol_new *kctl; - int cn; int i; if (!entity->num_sources) { @@ -611,18 +610,11 @@ static int entity_parse_mu(struct device *dev, dev_warn(dev, "%s: unexpected access layer: %x\n", entity->label, control->layers); - if (entity->num_sources != hweight64(control->cn_list)) { - dev_err(dev, "%s: mismatched control and sources\n", entity->label); - return -EINVAL; - } - kctl = devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL); if (!kctl) return -ENOMEM; - i = 0; - for_each_set_bit(cn, (unsigned long *)&control->cn_list, - BITS_PER_TYPE(control->cn_list)) { + for (i = 0; i < entity->num_sources; i++) { const char *control_name; struct soc_mixer_control *mc; @@ -647,7 +639,6 @@ static int entity_parse_mu(struct device *dev, kctl[i].info = snd_soc_info_volsw; kctl[i].get = snd_soc_dapm_get_volsw; kctl[i].put = snd_soc_dapm_put_volsw; - i++; } (*widget)->id = snd_soc_dapm_mixer; -- GitLab From a9302f8fbe8c0fd7d92e6e003ba62086b2ee1162 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Sat, 12 Jul 2025 21:59:03 +0200 Subject: [PATCH 804/868] dt-bindings: thermal: mediatek: Add fallback compatible string for MT7981 and MT8516 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ‘mediatek,mt7981-thermal’ and ‘mediatek,mt8516-thermal’ strings aren't definied in the driver. Both should have fallback compatible strings. This commit fixes this issue. Fixes: 788494ba0999 ("dt-bindings: thermal: convert Mediatek Thermal to the json-schema") Signed-off-by: Aleksander Jan Bajkowski Reviewed-by: Krzysztof Kozlowski Acked-by: Rafał Miłecki Link: https://lore.kernel.org/r/20250712195904.6988-2-olek2@wp.pl Signed-off-by: Daniel Lezcano --- .../bindings/thermal/mediatek,thermal.yaml | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml b/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml index d96a2e32bd8fd..7bd0955e6d045 100644 --- a/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/mediatek,thermal.yaml @@ -20,16 +20,23 @@ allOf: properties: compatible: - enum: - - mediatek,mt2701-thermal - - mediatek,mt2712-thermal - - mediatek,mt7622-thermal - - mediatek,mt7981-thermal - - mediatek,mt7986-thermal - - mediatek,mt8173-thermal - - mediatek,mt8183-thermal - - mediatek,mt8365-thermal - - mediatek,mt8516-thermal + oneOf: + - enum: + - mediatek,mt2701-thermal + - mediatek,mt2712-thermal + - mediatek,mt7622-thermal + - mediatek,mt7986-thermal + - mediatek,mt8173-thermal + - mediatek,mt8183-thermal + - mediatek,mt8365-thermal + - items: + - enum: + - mediatek,mt8516-thermal + - const: mediatek,mt2701-thermal + - items: + - enum: + - mediatek,mt7981-thermal + - const: mediatek,mt7986-thermal reg: maxItems: 1 -- GitLab From 1d264d3a198839c7483580acdce17e1015d0ef91 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Sun, 20 Jul 2025 21:14:59 -0500 Subject: [PATCH 805/868] dt-bindings: thermal: tegra: Document Tegra210B01 Add the compatible string for Tegra210B01 SOC_THERM Acked-by: Rob Herring (Arm) Signed-off-by: Aaron Kling Link: https://lore.kernel.org/r/20250720-t210b01-v2-5-9cb209f1edfc@gmail.com Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml index 19bb1f324183b..cf47a1f3b3847 100644 --- a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml +++ b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.yaml @@ -21,6 +21,7 @@ properties: - nvidia,tegra124-soctherm - nvidia,tegra132-soctherm - nvidia,tegra210-soctherm + - nvidia,tegra210b01-soctherm reg: maxItems: 2 @@ -207,6 +208,7 @@ allOf: enum: - nvidia,tegra124-soctherm - nvidia,tegra210-soctherm + - nvidia,tegra210b01-soctherm then: properties: reg: -- GitLab From 492086faa559e5b0bf776d2ade2497394d1ce151 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 21 Jul 2025 15:59:52 +0100 Subject: [PATCH 806/868] ACPI/PCI: Remove space before newline There is an extraneous space before a newline in an acpi_handle_debug() message. Remove it. Signed-off-by: Colin Ian King Acked-by: Bjorn Helgaas Link: https://patch.msgid.link/20250721145952.2601422-1-colin.i.king@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 08e10b6226dc6..e4560b33b8ad1 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -268,7 +268,7 @@ static int acpi_pci_link_get_current(struct acpi_pci_link *link) link->irq.active = irq; - acpi_handle_debug(handle, "Link at IRQ %d \n", link->irq.active); + acpi_handle_debug(handle, "Link at IRQ %d\n", link->irq.active); end: return result; -- GitLab From 5b838a24e9942d8b8da208f924701d0f989778cf Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 22 Jul 2025 11:27:54 +0100 Subject: [PATCH 807/868] ASoC: SDCA: Check devm_mutex_init() return value Fix interaction with commit daec29dcc873 ("locking/mutex: Mark devm_mutex_init() as __must_check"), add return value check. There is no need for additional complex error handling here, failure to init the mutex means the code can't progress, so the failure just needs to be passed up to the caller. Fixes: b126394d9ec6 ("ASoC: SDCA: Generic interrupt support") Signed-off-by: Stephen Rothwell Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20250722102754.2514351-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index b76512732af87..d442ba2f56816 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -419,7 +419,9 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev, info->irq_chip = sdca_irq_chip; - devm_mutex_init(dev, &info->irq_lock); + ret = devm_mutex_init(dev, &info->irq_lock); + if (ret) + return ERR_PTR(ret); ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0, &info->irq_chip, &info->irq_data); -- GitLab From 59c5dbd585a0bee70e51fcdf36185f7602b9c285 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 22 Jul 2025 11:23:05 +0100 Subject: [PATCH 808/868] ASoC: SDCA: Shrink detected_mode_handler() stack frame The stack frame for detected_mode_handler() is a bit large. Dynamically allocate the control value struct, which is most of the size, to avoid this. Fixes: b9ab3b618241 ("ASoC: SDCA: Add some initial IRQ handlers") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202507182222.OLgOy9fX-lkp@intel.com/ Signed-off-by: Charles Keepax Reviewed-by: Arnd Bergmann Link: https://patch.msgid.link/20250722102305.2513755-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_interrupts.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index d442ba2f56816..8018773ee4265 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -144,7 +144,7 @@ static irqreturn_t detected_mode_handler(int irq, void *data) struct snd_soc_card *card = component->card; struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem; struct snd_kcontrol *kctl = interrupt->priv; - struct snd_ctl_elem_value ucontrol; + struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; struct soc_enum *soc_enum; unsigned int reg, val; int ret; @@ -204,10 +204,14 @@ static irqreturn_t detected_mode_handler(int irq, void *data) dev_dbg(dev, "%s: %#x\n", interrupt->name, val); - ucontrol.value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); + ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL); + if (!ucontrol) + return IRQ_NONE; + + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); down_write(rwsem); - ret = kctl->put(kctl, &ucontrol); + ret = kctl->put(kctl, ucontrol); up_write(rwsem); if (ret < 0) { dev_err(dev, "failed to update selected mode: %d\n", ret); -- GitLab From cf685b3826e62a5e8bdc61135c0a60d128673e1b Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 15 Jul 2025 14:24:38 +0200 Subject: [PATCH 809/868] platform/x86: dell-uart-backlight: Use blacklight power constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backlight subsystem has gotten its own power constants. Replace FB_BLANK_UNBLANK with BACKLIGHT_POWER_ON. Signed-off-by: Thomas Zimmermann Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250715122643.137027-2-tzimmermann@suse.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-uart-backlight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c index 8f868f845350a..f323a667dc2d2 100644 --- a/drivers/platform/x86/dell/dell-uart-backlight.c +++ b/drivers/platform/x86/dell/dell-uart-backlight.c @@ -305,7 +305,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev) dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA); /* Initialize bl_power to a known value */ - ret = dell_uart_set_bl_power(dell_bl, FB_BLANK_UNBLANK); + ret = dell_uart_set_bl_power(dell_bl, BACKLIGHT_POWER_ON); if (ret) return ret; -- GitLab From 4ff3aeb664f7dfe824ba91ffb0b203397a8d431e Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 18 Jul 2025 12:23:05 -0500 Subject: [PATCH 810/868] platform/x86/amd: pmc: Add Lenovo Yoga 6 13ALC6 to pmc quirk list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo Yoga 6 13ACL6 82ND has a similar BIOS problem as other Lenovo laptops from that vintage that causes a rather long resume from suspend. Add it to the quirk list that manipulates the scratch register to avoid the issue. Reported-by: Adam Berglund Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4434 Tested-by: Adam Berglund Signed-off-by: Mario Limonciello Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20250718172307.1928744-1-superm1@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index 131f10b683088..ded4c84f5ed14 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -190,6 +190,15 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://gitlab.freedesktop.org/drm/amd/-/issues/4434 */ + { + .ident = "Lenovo Yoga 6 13ALC6", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82ND"), + } + }, /* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */ { .ident = "HP Laptop 15s-eq2xxx", -- GitLab From 246570cd351299959822ac21e75e2975f80ce4b7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 22 Jul 2025 12:47:05 +0100 Subject: [PATCH 811/868] ASoC: SDCA: Fix implicit cast from le16 As the HID wDescriptorLength is explicitly marked as little endian it should be converted to host endian before being used. Fixes: ac558015dfd8 ("ASoC: SDCA: add a HID device for HIDE entity") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202507221024.M18hWD6q-lkp@intel.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20250722114705.2816910-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c index 2224ade59affa..967f7ec6fb79d 100644 --- a/sound/soc/sdca/sdca_hid.c +++ b/sound/soc/sdca/sdca_hid.c @@ -24,7 +24,7 @@ static int sdwhid_parse(struct hid_device *hid) unsigned int rsize; int ret; - rsize = entity->hide.hid_desc.rpt_desc.wDescriptorLength; + rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize); -- GitLab From de2884c6cdd3d133704ce37393590dd1c761500c Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Wed, 2 Jul 2025 20:28:43 +0200 Subject: [PATCH 812/868] platform/x86: samsung-laptop: Expose charge_types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the newly introduced charge_types sysfs attribute as a replacement for the custom `battery_life_extender` attribute. Setting charge_types to `Long Life` enables battery life extending mode. This change is similar to the recent Ideapad patch adding support for charge_types. Signed-off-by: Jelle van der Waa Link: https://lore.kernel.org/r/20250702182844.107706-1-jvanderwaa@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../ABI/obsolete/sysfs-driver-samsung-laptop | 10 ++ .../ABI/testing/sysfs-driver-samsung-laptop | 11 -- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/samsung-laptop.c | 110 ++++++++++++++++++ 4 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 Documentation/ABI/obsolete/sysfs-driver-samsung-laptop diff --git a/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop b/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop new file mode 100644 index 0000000000000..204c3f3a1d781 --- /dev/null +++ b/Documentation/ABI/obsolete/sysfs-driver-samsung-laptop @@ -0,0 +1,10 @@ +What: /sys/devices/platform/samsung/battery_life_extender +Date: December 1, 2011 +KernelVersion: 3.3 +Contact: Corentin Chary +Description: Max battery charge level can be modified, battery cycle + life can be extended by reducing the max battery charge + level. + + - 0 means normal battery mode (100% charge) + - 1 means battery life extender mode (80% charge) diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop index 28c9c040de5d1..408cb0ddf4aa1 100644 --- a/Documentation/ABI/testing/sysfs-driver-samsung-laptop +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -20,17 +20,6 @@ Description: Some Samsung laptops have different "performance levels" and it's still unknown if this value even changes anything, other than making the user feel a bit better. -What: /sys/devices/platform/samsung/battery_life_extender -Date: December 1, 2011 -KernelVersion: 3.3 -Contact: Corentin Chary -Description: Max battery charge level can be modified, battery cycle - life can be extended by reducing the max battery charge - level. - - - 0 means normal battery mode (100% charge) - - 1 means battery life extender mode (80% charge) - What: /sys/devices/platform/samsung/usb_charge Date: December 1, 2011 KernelVersion: 3.3 diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 49ca98df47fd8..6d238e120dce7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -619,6 +619,7 @@ config SAMSUNG_LAPTOP tristate "Samsung Laptop driver" depends on RFKILL || RFKILL = n depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on ACPI_BATTERY depends on BACKLIGHT_CLASS_DEVICE select LEDS_CLASS select NEW_LEDS diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index decde4c9a3d97..9d43a12db73c8 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include /* @@ -348,6 +350,8 @@ struct samsung_laptop { struct notifier_block pm_nb; + struct acpi_battery_hook battery_hook; + bool handle_backlight; bool has_stepping_quirk; @@ -697,6 +701,11 @@ static ssize_t set_performance_level(struct device *dev, static DEVICE_ATTR(performance_level, 0644, get_performance_level, set_performance_level); +static void show_battery_life_extender_deprecation_warning(struct device *dev) +{ + dev_warn_once(dev, "battery_life_extender attribute has been deprecated, see charge_types.\n"); +} + static int read_battery_life_extender(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; @@ -739,6 +748,8 @@ static ssize_t get_battery_life_extender(struct device *dev, struct samsung_laptop *samsung = dev_get_drvdata(dev); int ret; + show_battery_life_extender_deprecation_warning(dev); + ret = read_battery_life_extender(samsung); if (ret < 0) return ret; @@ -753,6 +764,8 @@ static ssize_t set_battery_life_extender(struct device *dev, struct samsung_laptop *samsung = dev_get_drvdata(dev); int ret, value; + show_battery_life_extender_deprecation_warning(dev); + if (!count || kstrtoint(buf, 0, &value) != 0) return -EINVAL; @@ -766,6 +779,84 @@ static ssize_t set_battery_life_extender(struct device *dev, static DEVICE_ATTR(battery_life_extender, 0644, get_battery_life_extender, set_battery_life_extender); +static int samsung_psy_ext_set_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct samsung_laptop *samsung = ext_data; + + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + return write_battery_life_extender(samsung, 1); + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + return write_battery_life_extender(samsung, 0); + default: + return -EINVAL; + } +} + +static int samsung_psy_ext_get_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct samsung_laptop *samsung = ext_data; + int ret; + + ret = read_battery_life_extender(samsung); + if (ret < 0) + return ret; + + if (ret == 1) + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + else + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + + return 0; +} + +static int samsung_psy_prop_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp) +{ + return true; +} + +static const enum power_supply_property samsung_power_supply_props[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const struct power_supply_ext samsung_battery_ext = { + .name = "samsung_laptop", + .properties = samsung_power_supply_props, + .num_properties = ARRAY_SIZE(samsung_power_supply_props), + .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)), + .get_property = samsung_psy_ext_get_prop, + .set_property = samsung_psy_ext_set_prop, + .property_is_writeable = samsung_psy_prop_is_writeable, +}; + +static int samsung_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct samsung_laptop *samsung = container_of(hook, struct samsung_laptop, battery_hook); + + return power_supply_register_extension(battery, &samsung_battery_ext, + &samsung->platform_device->dev, samsung); +} + +static int samsung_battery_remove(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + power_supply_unregister_extension(battery, &samsung_battery_ext); + + return 0; +} + static int read_usb_charge(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; @@ -1043,6 +1134,21 @@ static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) return retval; } +static int __init samsung_battery_hook_init(struct samsung_laptop *samsung) +{ + int retval = 0; + + if (samsung->config->commands.get_battery_life_extender != 0xFFFF) { + samsung->battery_hook.add_battery = samsung_battery_add; + samsung->battery_hook.remove_battery = samsung_battery_remove; + samsung->battery_hook.name = "Samsung Battery Extension"; + retval = devm_battery_hook_register(&samsung->platform_device->dev, + &samsung->battery_hook); + } + + return retval; +} + static int kbd_backlight_enable(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; @@ -1604,6 +1710,10 @@ static int __init samsung_init(void) if (ret) goto error_lid_handling; + ret = samsung_battery_hook_init(samsung); + if (ret) + goto error_lid_handling; + samsung_debugfs_init(samsung); samsung->pm_nb.notifier_call = samsung_pm_notification; -- GitLab From 54d5cd4719c5e87f33d271c9ac2e393147d934f8 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:31 -0400 Subject: [PATCH 813/868] platform/x86/intel/pmt: fix a crashlog NULL pointer access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usage of the intel_pmt_read() for binary sysfs, requires a pcidev. The current use of the endpoint value is only valid for telemetry endpoint usage. Without the ep, the crashlog usage causes the following NULL pointer exception: BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: Oops: 0000 [#1] SMP NOPTI RIP: 0010:intel_pmt_read+0x3b/0x70 [pmt_class] Code: Call Trace: ? sysfs_kf_bin_read+0xc0/0xe0 kernfs_fop_read_iter+0xac/0x1a0 vfs_read+0x26d/0x350 ksys_read+0x6b/0xe0 __x64_sys_read+0x1d/0x30 x64_sys_call+0x1bc8/0x1d70 do_syscall_64+0x6d/0x110 Augment struct intel_pmt_entry with a pointer to the pcidev to avoid the NULL pointer exception. Fixes: 045a513040cc ("platform/x86/intel/pmt: Use PMT callbacks") Cc: stable@vger.kernel.org Reviewed-by: David E. Box Reviewed-by: Tejas Upadhyay Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-2-michael.j.ruhl@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 3 ++- drivers/platform/x86/intel/pmt/class.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index a806a81ece520..5dd80109f5917 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -99,7 +99,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, if (count > entry->size - off) count = entry->size - off; - count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf, + count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf, entry->base, off, count); return count; @@ -283,6 +283,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, return -EINVAL; } + entry->pcidev = pci_dev; entry->guid = header->guid; entry->size = header->size; entry->cb = ivdev->priv_data; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index fdf7e79d8c0dc..ebd49e2cb2d57 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -40,6 +40,7 @@ struct intel_pmt_header { struct intel_pmt_entry { struct telem_endpoint *ep; + struct pci_dev *pcidev; struct intel_pmt_header header; struct bin_attribute pmt_bin_attr; struct kobject *kobj; -- GitLab From 0ba9e9cf76f2487654bc9bca38218780fa53030e Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:32 -0400 Subject: [PATCH 814/868] drm/xe: Correct the rev value for the DVSEC entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By definition, the Designated Vendor Specific Extended Capability (DVSEC) revision should be 1. Add the rev value to be correct. Fixes: 0c45e76fcc62 ("drm/xe/vsec: Support BMG devices") Signed-off-by: Michael J. Ruhl Reviewed-by: David E. Box Link: https://lore.kernel.org/r/20250713172943.7335-3-michael.j.ruhl@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/gpu/drm/xe/xe_vsec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index b378848d3b7bc..1bf7e709e1100 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -24,6 +24,7 @@ #define BMG_DEVICE_ID 0xE2F8 static struct intel_vsec_header bmg_telemetry = { + .rev = 1, .length = 0x10, .id = VSEC_ID_TELEMETRY, .num_entries = 2, @@ -33,6 +34,7 @@ static struct intel_vsec_header bmg_telemetry = { }; static struct intel_vsec_header bmg_punit_crashlog = { + .rev = 1, .length = 0x10, .id = VSEC_ID_CRASHLOG, .num_entries = 1, @@ -42,6 +44,7 @@ static struct intel_vsec_header bmg_punit_crashlog = { }; static struct intel_vsec_header bmg_oobmsm_crashlog = { + .rev = 1, .length = 0x10, .id = VSEC_ID_CRASHLOG, .num_entries = 1, -- GitLab From 5b27388171a18cf6842c700520086ec50194e858 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:33 -0400 Subject: [PATCH 815/868] drm/xe: Correct BMG VSEC header sizing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intel_vsec_header information for the crashlog feature is incorrect. Update the VSEC header with correct sizing and count. Since the crashlog entries are "merged" (num_entries = 2), the separate capabilities entries must be merged as well. Fixes: 0c45e76fcc62 ("drm/xe/vsec: Support BMG devices") Acked-by: Rodrigo Vivi Signed-off-by: Michael J. Ruhl Reviewed-by: David E. Box Link: https://lore.kernel.org/r/20250713172943.7335-4-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/gpu/drm/xe/xe_vsec.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index 1bf7e709e1100..56930ad429621 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -33,30 +33,19 @@ static struct intel_vsec_header bmg_telemetry = { .offset = BMG_DISCOVERY_OFFSET, }; -static struct intel_vsec_header bmg_punit_crashlog = { +static struct intel_vsec_header bmg_crashlog = { .rev = 1, .length = 0x10, .id = VSEC_ID_CRASHLOG, - .num_entries = 1, - .entry_size = 4, + .num_entries = 2, + .entry_size = 6, .tbir = 0, .offset = BMG_DISCOVERY_OFFSET + 0x60, }; -static struct intel_vsec_header bmg_oobmsm_crashlog = { - .rev = 1, - .length = 0x10, - .id = VSEC_ID_CRASHLOG, - .num_entries = 1, - .entry_size = 4, - .tbir = 0, - .offset = BMG_DISCOVERY_OFFSET + 0x78, -}; - static struct intel_vsec_header *bmg_capabilities[] = { &bmg_telemetry, - &bmg_punit_crashlog, - &bmg_oobmsm_crashlog, + &bmg_crashlog, NULL }; -- GitLab From ba22fe0cffedf5156731fbac729a638f18d17e6b Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:34 -0400 Subject: [PATCH 816/868] platform/x86/intel/pmt: white space cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed two white space issues; cleaned them. Reviewed-by: David E. Box Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-5-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 6a9eb3c4b3137..d40c8e212733e 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -143,7 +143,7 @@ enable_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct crashlog_entry *entry; bool enabled; @@ -177,7 +177,7 @@ trigger_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t trigger_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct crashlog_entry *entry; bool trigger; -- GitLab From 75a496aa054326d9ebf27b39e1af8b5b770311ba Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:35 -0400 Subject: [PATCH 817/868] platform/x86/intel/pmt: mutex clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header file for mutex usage and mutex_destroy() cleanup code is absent from the crashlog.c module. Add the header file and mutex_destroy(). Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-6-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index d40c8e212733e..6e32fc1f8f1d8 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -262,8 +263,12 @@ static void pmt_crashlog_remove(struct auxiliary_device *auxdev) struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); int i; - for (i = 0; i < priv->num_entries; i++) - intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); + for (i = 0; i < priv->num_entries; i++) { + struct crashlog_entry *crashlog = &priv->entry[i]; + + intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns); + mutex_destroy(&crashlog->control_mutex); + } } static int pmt_crashlog_probe(struct auxiliary_device *auxdev, -- GitLab From 4f8fa22d108006b8ec0b94bb67a1bd537a2bf141 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:36 -0400 Subject: [PATCH 818/868] platform/x86/intel/pmt: use guard(mutex) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the mutex paths to use the new guard() mechanism. With the removal of goto, do some minor cleanup of the current logic path. Reviewed-by: David E. Box Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-7-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 6e32fc1f8f1d8..c3ca95854aba9 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -156,9 +157,9 @@ enable_store(struct device *dev, struct device_attribute *attr, if (result) return result; - mutex_lock(&entry->control_mutex); + guard(mutex)(&entry->control_mutex); + pmt_crashlog_set_disable(&entry->entry, !enabled); - mutex_unlock(&entry->control_mutex); return count; } @@ -190,26 +191,24 @@ trigger_store(struct device *dev, struct device_attribute *attr, if (result) return result; - mutex_lock(&entry->control_mutex); + guard(mutex)(&entry->control_mutex); if (!trigger) { pmt_crashlog_set_clear(&entry->entry); - } else if (pmt_crashlog_complete(&entry->entry)) { - /* we cannot trigger a new crash if one is still pending */ - result = -EEXIST; - goto err; - } else if (pmt_crashlog_disabled(&entry->entry)) { - /* if device is currently disabled, return busy */ - result = -EBUSY; - goto err; - } else { - pmt_crashlog_set_execute(&entry->entry); + return count; } - result = count; -err: - mutex_unlock(&entry->control_mutex); - return result; + /* we cannot trigger a new crash if one is still pending */ + if (pmt_crashlog_complete(&entry->entry)) + return -EEXIST; + + /* if device is currently disabled, return busy */ + if (pmt_crashlog_disabled(&entry->entry)) + return -EBUSY; + + pmt_crashlog_set_execute(&entry->entry); + + return count; } static DEVICE_ATTR_RW(trigger); -- GitLab From 147c18d8efaa1fa006fdd0b901d51092dc3b2189 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:37 -0400 Subject: [PATCH 819/868] platform/x86/intel/pmt: re-order trigger logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting the clear bit or checking the complete bit before checking to see if crashlog is disabled seems incorrect. Check disable before accessing any other bits. Reviewed-by: Ilpo Järvinen Reviewed-by: David E. Box Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-8-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index c3ca95854aba9..440d2045e90d0 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -193,6 +193,10 @@ trigger_store(struct device *dev, struct device_attribute *attr, guard(mutex)(&entry->control_mutex); + /* if device is currently disabled, return busy */ + if (pmt_crashlog_disabled(&entry->entry)) + return -EBUSY; + if (!trigger) { pmt_crashlog_set_clear(&entry->entry); return count; @@ -202,10 +206,6 @@ trigger_store(struct device *dev, struct device_attribute *attr, if (pmt_crashlog_complete(&entry->entry)) return -EEXIST; - /* if device is currently disabled, return busy */ - if (pmt_crashlog_disabled(&entry->entry)) - return -EBUSY; - pmt_crashlog_set_execute(&entry->entry); return count; -- GitLab From 5c7bfa1088274bc3820eb2083f8091ff8ad397ec Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:38 -0400 Subject: [PATCH 820/868] platform/x86/intel/pmt: correct types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A couple of local variables do not match the return types of some of the functions. Update the mismatched types to match. Reviewed-by: Ilpo Järvinen Reviewed-by: David E. Box Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-9-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 440d2045e90d0..881f4abdae14f 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -138,7 +138,7 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct intel_pmt_entry *entry = dev_get_drvdata(dev); - int enabled = !pmt_crashlog_disabled(entry); + bool enabled = !pmt_crashlog_disabled(entry); return sprintf(buf, "%d\n", enabled); } @@ -169,7 +169,7 @@ static ssize_t trigger_show(struct device *dev, struct device_attribute *attr, char *buf) { struct intel_pmt_entry *entry; - int trigger; + bool trigger; entry = dev_get_drvdata(dev); trigger = pmt_crashlog_complete(entry); -- GitLab From 8ab4f88d46c70bade0633b56a0f03e20102bf10c Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:39 -0400 Subject: [PATCH 821/868] platform/x86/intel/pmt: decouple sysfs and namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PMT namespace includes the crashlog sysfs attribute information. Other crashlog version/types may need different sysfs attributes. Coupling the attributes with the namespace blocks this usage. Decouple sysfs attributes from the name space and add them to the specific entry. Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-10-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 12 ++++++------ drivers/platform/x86/intel/pmt/class.h | 2 +- drivers/platform/x86/intel/pmt/crashlog.c | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 5dd80109f5917..edcce340ea674 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -316,8 +316,8 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, entry->kobj = &dev->kobj; - if (ns->attr_grp) { - ret = sysfs_create_group(entry->kobj, ns->attr_grp); + if (entry->attr_grp) { + ret = sysfs_create_group(entry->kobj, entry->attr_grp); if (ret) goto fail_sysfs_create_group; } @@ -358,8 +358,8 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, fail_add_endpoint: sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); fail_ioremap: - if (ns->attr_grp) - sysfs_remove_group(entry->kobj, ns->attr_grp); + if (entry->attr_grp) + sysfs_remove_group(entry->kobj, entry->attr_grp); fail_sysfs_create_group: device_unregister(dev); fail_dev_create: @@ -401,8 +401,8 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, if (entry->size) sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); - if (ns->attr_grp) - sysfs_remove_group(entry->kobj, ns->attr_grp); + if (entry->attr_grp) + sysfs_remove_group(entry->kobj, entry->attr_grp); device_unregister(dev); xa_erase(ns->xa, entry->devid); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index ebd49e2cb2d57..3c5ad5f52bca6 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -43,6 +43,7 @@ struct intel_pmt_entry { struct pci_dev *pcidev; struct intel_pmt_header header; struct bin_attribute pmt_bin_attr; + const struct attribute_group *attr_grp; struct kobject *kobj; void __iomem *disc_table; void __iomem *base; @@ -58,7 +59,6 @@ struct intel_pmt_entry { struct intel_pmt_namespace { const char *name; struct xarray *xa; - const struct attribute_group *attr_grp; int (*pmt_header_decode)(struct intel_pmt_entry *entry, struct device *dev); int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev, diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 881f4abdae14f..23b3971da40ac 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -243,6 +243,8 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, /* Size is measured in DWORDS, but accessor returns bytes */ header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); + entry->attr_grp = &pmt_crashlog_group; + return 0; } @@ -250,7 +252,6 @@ static DEFINE_XARRAY_ALLOC(crashlog_array); static struct intel_pmt_namespace pmt_crashlog_ns = { .name = "crashlog", .xa = &crashlog_array, - .attr_grp = &pmt_crashlog_group, .pmt_header_decode = pmt_crashlog_header_decode, }; -- GitLab From f57b32cb4adb28b62f61c4729f7b85f55518cb2b Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:40 -0400 Subject: [PATCH 822/868] platform/x86/intel/pmt: add register access helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The control register is used in a read/modify/write pattern. The status register is used in a read/check bit pattern. Add helpers to eliminate common code. Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-11-michael.j.ruhl@intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 58 +++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 23b3971da40ac..ed781548e59d2 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -64,20 +64,40 @@ struct pmt_crashlog_priv { /* * I/O */ -static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) + +/* Read, modify, write the control register, setting or clearing @bit based on @set */ +static void pmt_crashlog_rmw(struct intel_pmt_entry *entry, u32 bit, bool set) { - u32 control = readl(entry->disc_table + CONTROL_OFFSET); + u32 reg = readl(entry->disc_table + CONTROL_OFFSET); + + reg &= ~CRASHLOG_FLAG_TRIGGER_MASK; + + if (set) + reg |= bit; + else + reg &= ~bit; + + writel(reg, entry->disc_table + CONTROL_OFFSET); +} + +/* Read the status register and see if the specified @bit is set */ +static bool pmt_crashlog_rc(struct intel_pmt_entry *entry, u32 bit) +{ + u32 reg = readl(entry->disc_table + CONTROL_OFFSET); + + return !!(reg & bit); +} +static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) +{ /* return current value of the crashlog complete flag */ - return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE); + return pmt_crashlog_rc(entry, CRASHLOG_FLAG_TRIGGER_COMPLETE); } static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) { - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - /* return current value of the crashlog disabled flag */ - return !!(control & CRASHLOG_FLAG_DISABLE); + return pmt_crashlog_rc(entry, CRASHLOG_FLAG_DISABLE); } static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) @@ -98,37 +118,17 @@ static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, bool disable) { - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - /* clear trigger bits so we are only modifying disable flag */ - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - - if (disable) - control |= CRASHLOG_FLAG_DISABLE; - else - control &= ~CRASHLOG_FLAG_DISABLE; - - writel(control, entry->disc_table + CONTROL_OFFSET); + pmt_crashlog_rmw(entry, CRASHLOG_FLAG_DISABLE, disable); } static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) { - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - control |= CRASHLOG_FLAG_TRIGGER_CLEAR; - - writel(control, entry->disc_table + CONTROL_OFFSET); + pmt_crashlog_rmw(entry, CRASHLOG_FLAG_TRIGGER_CLEAR, true); } static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) { - u32 control = readl(entry->disc_table + CONTROL_OFFSET); - - control &= ~CRASHLOG_FLAG_TRIGGER_MASK; - control |= CRASHLOG_FLAG_TRIGGER_EXECUTE; - - writel(control, entry->disc_table + CONTROL_OFFSET); + pmt_crashlog_rmw(entry, CRASHLOG_FLAG_TRIGGER_EXECUTE, true); } /* -- GitLab From 66df9fa783aadc2a5ae8ca11ead0b13032d24e7e Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:41 -0400 Subject: [PATCH 823/868] platform/x86/intel/pmt: refactor base parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support an upcoming crashlog change, use the parent of struct intel_pmt_entry, struct crashlog_entry, as a main parameter. Using struct crashlog_entry will allow for a more flexible interface to control the crashlog feature. - Refactor to use struct crashlog_entry in place of struct intel_pmt_entry - Rename variables from "entry" to "crashlog" Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-12-michael.j.ruhl@intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 58 ++++++++++++----------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index ed781548e59d2..087b7110ddd26 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -66,8 +66,9 @@ struct pmt_crashlog_priv { */ /* Read, modify, write the control register, setting or clearing @bit based on @set */ -static void pmt_crashlog_rmw(struct intel_pmt_entry *entry, u32 bit, bool set) +static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set) { + struct intel_pmt_entry *entry = &crashlog->entry; u32 reg = readl(entry->disc_table + CONTROL_OFFSET); reg &= ~CRASHLOG_FLAG_TRIGGER_MASK; @@ -81,23 +82,24 @@ static void pmt_crashlog_rmw(struct intel_pmt_entry *entry, u32 bit, bool set) } /* Read the status register and see if the specified @bit is set */ -static bool pmt_crashlog_rc(struct intel_pmt_entry *entry, u32 bit) +static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) { + struct intel_pmt_entry *entry = &crashlog->entry; u32 reg = readl(entry->disc_table + CONTROL_OFFSET); return !!(reg & bit); } -static bool pmt_crashlog_complete(struct intel_pmt_entry *entry) +static bool pmt_crashlog_complete(struct crashlog_entry *crashlog) { /* return current value of the crashlog complete flag */ - return pmt_crashlog_rc(entry, CRASHLOG_FLAG_TRIGGER_COMPLETE); + return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_TRIGGER_COMPLETE); } -static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry) +static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) { /* return current value of the crashlog disabled flag */ - return pmt_crashlog_rc(entry, CRASHLOG_FLAG_DISABLE); + return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_DISABLE); } static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) @@ -115,20 +117,20 @@ static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) return crash_type == CRASH_TYPE_OOBMSM && version == 0; } -static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry, +static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, bool disable) { - pmt_crashlog_rmw(entry, CRASHLOG_FLAG_DISABLE, disable); + pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_DISABLE, disable); } -static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry) +static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog) { - pmt_crashlog_rmw(entry, CRASHLOG_FLAG_TRIGGER_CLEAR, true); + pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_CLEAR, true); } -static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) +static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) { - pmt_crashlog_rmw(entry, CRASHLOG_FLAG_TRIGGER_EXECUTE, true); + pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_EXECUTE, true); } /* @@ -137,8 +139,8 @@ static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry) static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_pmt_entry *entry = dev_get_drvdata(dev); - bool enabled = !pmt_crashlog_disabled(entry); + struct crashlog_entry *crashlog = dev_get_drvdata(dev); + bool enabled = !pmt_crashlog_disabled(crashlog); return sprintf(buf, "%d\n", enabled); } @@ -147,19 +149,19 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct crashlog_entry *entry; + struct crashlog_entry *crashlog; bool enabled; int result; - entry = dev_get_drvdata(dev); + crashlog = dev_get_drvdata(dev); result = kstrtobool(buf, &enabled); if (result) return result; - guard(mutex)(&entry->control_mutex); + guard(mutex)(&crashlog->control_mutex); - pmt_crashlog_set_disable(&entry->entry, !enabled); + pmt_crashlog_set_disable(crashlog, !enabled); return count; } @@ -168,11 +170,11 @@ static DEVICE_ATTR_RW(enable); static ssize_t trigger_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct intel_pmt_entry *entry; + struct crashlog_entry *crashlog; bool trigger; - entry = dev_get_drvdata(dev); - trigger = pmt_crashlog_complete(entry); + crashlog = dev_get_drvdata(dev); + trigger = pmt_crashlog_complete(crashlog); return sprintf(buf, "%d\n", trigger); } @@ -181,32 +183,32 @@ static ssize_t trigger_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct crashlog_entry *entry; + struct crashlog_entry *crashlog; bool trigger; int result; - entry = dev_get_drvdata(dev); + crashlog = dev_get_drvdata(dev); result = kstrtobool(buf, &trigger); if (result) return result; - guard(mutex)(&entry->control_mutex); + guard(mutex)(&crashlog->control_mutex); /* if device is currently disabled, return busy */ - if (pmt_crashlog_disabled(&entry->entry)) + if (pmt_crashlog_disabled(crashlog)) return -EBUSY; if (!trigger) { - pmt_crashlog_set_clear(&entry->entry); + pmt_crashlog_set_clear(crashlog); return count; } /* we cannot trigger a new crash if one is still pending */ - if (pmt_crashlog_complete(&entry->entry)) + if (pmt_crashlog_complete(crashlog)) return -EEXIST; - pmt_crashlog_set_execute(&entry->entry); + pmt_crashlog_set_execute(crashlog); return count; } -- GitLab From 5623fa6859a6cd49366421317e3c5ab183583624 Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:42 -0400 Subject: [PATCH 824/868] platform/x86/intel/pmt: use a version struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for supporting multiple crashlog versions, use a struct to keep bit offset info for the status and control bits. Reviewed-by: Ilpo Järvinen Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-13-michael.j.ruhl@intel.com [ij: move crashlog_type1_ver0 to its final place & add consts to crashlog_info] Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 92 ++++++++++++++++------- 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 087b7110ddd26..db54f881ba576 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -24,21 +24,6 @@ /* Crashlog discovery header types */ #define CRASH_TYPE_OOBMSM 1 -/* Control Flags */ -#define CRASHLOG_FLAG_DISABLE BIT(28) - -/* - * Bits 29 and 30 control the state of bit 31. - * - * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. - * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. - * Bit 31 is the read-only status with a 1 indicating log is complete. - */ -#define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29) -#define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30) -#define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) -#define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) - /* Crashlog Discovery Header */ #define CONTROL_OFFSET 0x0 #define GUID_OFFSET 0x4 @@ -50,10 +35,50 @@ /* size is in bytes */ #define GET_SIZE(v) ((v) * sizeof(u32)) +/* + * Type 1 Version 0 + * status and control registers are combined. + * + * Bits 29 and 30 control the state of bit 31. + * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. + * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. + * Bit 31 is the read-only status with a 1 indicating log is complete. + */ +#define TYPE1_VER0_STATUS_OFFSET 0x00 +#define TYPE1_VER0_CONTROL_OFFSET 0x00 + +#define TYPE1_VER0_DISABLE BIT(28) +#define TYPE1_VER0_CLEAR BIT(29) +#define TYPE1_VER0_EXECUTE BIT(30) +#define TYPE1_VER0_COMPLETE BIT(31) +#define TYPE1_VER0_TRIGGER_MASK GENMASK(31, 28) + +/* After offset, order alphabetically, not bit ordered */ +struct crashlog_status { + u32 offset; + u32 cleared; + u32 complete; + u32 disabled; +}; + +struct crashlog_control { + u32 offset; + u32 trigger_mask; + u32 clear; + u32 disable; + u32 manual; +}; + +struct crashlog_info { + const struct crashlog_status status; + const struct crashlog_control control; +}; + struct crashlog_entry { /* entry must be first member of struct */ struct intel_pmt_entry entry; struct mutex control_mutex; + const struct crashlog_info *info; }; struct pmt_crashlog_priv { @@ -68,24 +93,25 @@ struct pmt_crashlog_priv { /* Read, modify, write the control register, setting or clearing @bit based on @set */ static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set) { + const struct crashlog_control *control = &crashlog->info->control; struct intel_pmt_entry *entry = &crashlog->entry; - u32 reg = readl(entry->disc_table + CONTROL_OFFSET); + u32 reg = readl(entry->disc_table + control->offset); - reg &= ~CRASHLOG_FLAG_TRIGGER_MASK; + reg &= ~control->trigger_mask; if (set) reg |= bit; else reg &= ~bit; - writel(reg, entry->disc_table + CONTROL_OFFSET); + writel(reg, entry->disc_table + control->offset); } /* Read the status register and see if the specified @bit is set */ static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) { - struct intel_pmt_entry *entry = &crashlog->entry; - u32 reg = readl(entry->disc_table + CONTROL_OFFSET); + const struct crashlog_status *status = &crashlog->info->status; + u32 reg = readl(crashlog->entry.disc_table + status->offset); return !!(reg & bit); } @@ -93,13 +119,13 @@ static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) static bool pmt_crashlog_complete(struct crashlog_entry *crashlog) { /* return current value of the crashlog complete flag */ - return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_TRIGGER_COMPLETE); + return pmt_crashlog_rc(crashlog, crashlog->info->status.complete); } static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) { /* return current value of the crashlog disabled flag */ - return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_DISABLE); + return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled); } static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) @@ -120,17 +146,17 @@ static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, bool disable) { - pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_DISABLE, disable); + pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable); } static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog) { - pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_CLEAR, true); + pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true); } static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) { - pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_EXECUTE, true); + pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true); } /* @@ -224,6 +250,19 @@ static const struct attribute_group pmt_crashlog_group = { .attrs = pmt_crashlog_attrs, }; +static const struct crashlog_info crashlog_type1_ver0 = { + .status.offset = TYPE1_VER0_STATUS_OFFSET, + .status.cleared = TYPE1_VER0_CLEAR, + .status.complete = TYPE1_VER0_COMPLETE, + .status.disabled = TYPE1_VER0_DISABLE, + + .control.offset = TYPE1_VER0_CONTROL_OFFSET, + .control.trigger_mask = TYPE1_VER0_TRIGGER_MASK, + .control.clear = TYPE1_VER0_CLEAR, + .control.disable = TYPE1_VER0_DISABLE, + .control.manual = TYPE1_VER0_EXECUTE, +}; + static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, struct device *dev) { @@ -234,9 +273,10 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, if (!pmt_crashlog_supported(entry)) return 1; - /* initialize control mutex */ + /* initialize the crashlog struct */ crashlog = container_of(entry, struct crashlog_entry, entry); mutex_init(&crashlog->control_mutex); + crashlog->info = &crashlog_type1_ver0; header->access_type = GET_ACCESS(readl(disc_table)); header->guid = readl(disc_table + GUID_OFFSET); -- GitLab From 2c402a801c19568de97b86a77b25d13448dc080a Mon Sep 17 00:00:00 2001 From: "Michael J. Ruhl" Date: Sun, 13 Jul 2025 13:29:43 -0400 Subject: [PATCH 825/868] platform/x86/intel/pmt: support BMG crashlog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Battlemage GPU has the type 1 version 2 crashlog feature. Update the crashlog driver to support this crashlog version. Signed-off-by: Michael J. Ruhl Link: https://lore.kernel.org/r/20250713172943.7335-14-michael.j.ruhl@intel.com [ij: make crashlog_type1_ver2 static] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 262 ++++++++++++++++++++-- 1 file changed, 249 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index db54f881ba576..b0393c9c5b4b2 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -53,25 +53,59 @@ #define TYPE1_VER0_COMPLETE BIT(31) #define TYPE1_VER0_TRIGGER_MASK GENMASK(31, 28) +/* + * Type 1 Version 2 + * status and control are different registers + */ +#define TYPE1_VER2_STATUS_OFFSET 0x00 +#define TYPE1_VER2_CONTROL_OFFSET 0x14 + +/* status register */ +#define TYPE1_VER2_CLEAR_SUPPORT BIT(20) +#define TYPE1_VER2_REARMED BIT(25) +#define TYPE1_VER2_ERROR BIT(26) +#define TYPE1_VER2_CONSUMED BIT(27) +#define TYPE1_VER2_DISABLED BIT(28) +#define TYPE1_VER2_CLEARED BIT(29) +#define TYPE1_VER2_IN_PROGRESS BIT(30) +#define TYPE1_VER2_COMPLETE BIT(31) + +/* control register */ +#define TYPE1_VER2_CONSUME BIT(25) +#define TYPE1_VER2_REARM BIT(28) +#define TYPE1_VER2_EXECUTE BIT(29) +#define TYPE1_VER2_CLEAR BIT(30) +#define TYPE1_VER2_DISABLE BIT(31) +#define TYPE1_VER2_TRIGGER_MASK \ + (TYPE1_VER2_EXECUTE | TYPE1_VER2_CLEAR | TYPE1_VER2_DISABLE) + /* After offset, order alphabetically, not bit ordered */ struct crashlog_status { u32 offset; + u32 clear_supported; u32 cleared; u32 complete; + u32 consumed; u32 disabled; + u32 error; + u32 in_progress; + u32 rearmed; }; struct crashlog_control { u32 offset; u32 trigger_mask; u32 clear; + u32 consume; u32 disable; u32 manual; + u32 rearm; }; struct crashlog_info { const struct crashlog_status status; const struct crashlog_control control; + const struct attribute_group *attr_grp; }; struct crashlog_entry { @@ -128,19 +162,23 @@ static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled); } -static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) +static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version) { u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); - u32 crash_type, version; - crash_type = GET_TYPE(discovery_header); - version = GET_VERSION(discovery_header); + *crash_type = GET_TYPE(discovery_header); + *version = GET_VERSION(discovery_header); /* - * Currently we only recognize OOBMSM version 0 devices. - * We can ignore all other crashlog devices in the system. + * Currently we only recognize OOBMSM (type 1) and version 0 or 2 + * devices. + * + * Ignore all other crashlog devices in the system. */ - return crash_type == CRASH_TYPE_OOBMSM && version == 0; + if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2)) + return true; + + return false; } static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, @@ -159,9 +197,115 @@ static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true); } +static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog) +{ + return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared); +} + +static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog) +{ + return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed); +} + +static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog) +{ + pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true); +} + +static bool pmt_crashlog_error(struct crashlog_entry *crashlog) +{ + return pmt_crashlog_rc(crashlog, crashlog->info->status.error); +} + +static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog) +{ + return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed); +} + +static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog) +{ + pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true); +} + /* * sysfs */ +static ssize_t +clear_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct crashlog_entry *crashlog = dev_get_drvdata(dev); + bool cleared = pmt_crashlog_cleared(crashlog); + + return sysfs_emit(buf, "%d\n", cleared); +} + +static ssize_t +clear_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct crashlog_entry *crashlog; + bool clear; + int result; + + crashlog = dev_get_drvdata(dev); + + result = kstrtobool(buf, &clear); + if (result) + return result; + + /* set bit only */ + if (!clear) + return -EINVAL; + + guard(mutex)(&crashlog->control_mutex); + + pmt_crashlog_set_clear(crashlog); + + return count; +} +static DEVICE_ATTR_RW(clear); + +static ssize_t +consumed_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct crashlog_entry *crashlog = dev_get_drvdata(dev); + bool consumed = pmt_crashlog_consumed(crashlog); + + return sysfs_emit(buf, "%d\n", consumed); +} + +static ssize_t +consumed_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct crashlog_entry *crashlog; + bool consumed; + int result; + + crashlog = dev_get_drvdata(dev); + + result = kstrtobool(buf, &consumed); + if (result) + return result; + + /* set bit only */ + if (!consumed) + return -EINVAL; + + guard(mutex)(&crashlog->control_mutex); + + if (pmt_crashlog_disabled(crashlog)) + return -EBUSY; + + if (!pmt_crashlog_complete(crashlog)) + return -EEXIST; + + pmt_crashlog_set_consumed(crashlog); + + return count; +} +static DEVICE_ATTR_RW(consumed); + static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -193,6 +337,51 @@ enable_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(enable); +static ssize_t +error_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct crashlog_entry *crashlog = dev_get_drvdata(dev); + bool error = pmt_crashlog_error(crashlog); + + return sysfs_emit(buf, "%d\n", error); +} +static DEVICE_ATTR_RO(error); + +static ssize_t +rearm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct crashlog_entry *crashlog = dev_get_drvdata(dev); + int rearmed = pmt_crashlog_rearm(crashlog); + + return sysfs_emit(buf, "%d\n", rearmed); +} + +static ssize_t +rearm_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct crashlog_entry *crashlog; + bool rearm; + int result; + + crashlog = dev_get_drvdata(dev); + + result = kstrtobool(buf, &rearm); + if (result) + return result; + + /* set only */ + if (!rearm) + return -EINVAL; + + guard(mutex)(&crashlog->control_mutex); + + pmt_crashlog_set_rearm(crashlog); + + return count; +} +static DEVICE_ATTR_RW(rearm); + static ssize_t trigger_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -240,14 +429,28 @@ trigger_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(trigger); -static struct attribute *pmt_crashlog_attrs[] = { +static struct attribute *pmt_crashlog_type1_ver0_attrs[] = { &dev_attr_enable.attr, &dev_attr_trigger.attr, NULL }; -static const struct attribute_group pmt_crashlog_group = { - .attrs = pmt_crashlog_attrs, +static struct attribute *pmt_crashlog_type1_ver2_attrs[] = { + &dev_attr_clear.attr, + &dev_attr_consumed.attr, + &dev_attr_enable.attr, + &dev_attr_error.attr, + &dev_attr_rearm.attr, + &dev_attr_trigger.attr, + NULL +}; + +static const struct attribute_group pmt_crashlog_type1_ver0_group = { + .attrs = pmt_crashlog_type1_ver0_attrs, +}; + +static const struct attribute_group pmt_crashlog_type1_ver2_group = { + .attrs = pmt_crashlog_type1_ver2_attrs, }; static const struct crashlog_info crashlog_type1_ver0 = { @@ -261,22 +464,55 @@ static const struct crashlog_info crashlog_type1_ver0 = { .control.clear = TYPE1_VER0_CLEAR, .control.disable = TYPE1_VER0_DISABLE, .control.manual = TYPE1_VER0_EXECUTE, + .attr_grp = &pmt_crashlog_type1_ver0_group, }; +static const struct crashlog_info crashlog_type1_ver2 = { + .status.offset = TYPE1_VER2_STATUS_OFFSET, + .status.clear_supported = TYPE1_VER2_CLEAR_SUPPORT, + .status.cleared = TYPE1_VER2_CLEARED, + .status.complete = TYPE1_VER2_COMPLETE, + .status.consumed = TYPE1_VER2_CONSUMED, + .status.disabled = TYPE1_VER2_DISABLED, + .status.error = TYPE1_VER2_ERROR, + .status.in_progress = TYPE1_VER2_IN_PROGRESS, + .status.rearmed = TYPE1_VER2_REARMED, + + .control.offset = TYPE1_VER2_CONTROL_OFFSET, + .control.trigger_mask = TYPE1_VER2_TRIGGER_MASK, + .control.clear = TYPE1_VER2_CLEAR, + .control.consume = TYPE1_VER2_CONSUME, + .control.disable = TYPE1_VER2_DISABLE, + .control.manual = TYPE1_VER2_EXECUTE, + .control.rearm = TYPE1_VER2_REARM, + .attr_grp = &pmt_crashlog_type1_ver2_group, +}; + +static const struct crashlog_info *select_crashlog_info(u32 type, u32 version) +{ + if (version == 0) + return &crashlog_type1_ver0; + + return &crashlog_type1_ver2; +} + static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, struct device *dev) { void __iomem *disc_table = entry->disc_table; struct intel_pmt_header *header = &entry->header; struct crashlog_entry *crashlog; + u32 version; + u32 type; - if (!pmt_crashlog_supported(entry)) + if (!pmt_crashlog_supported(entry, &type, &version)) return 1; /* initialize the crashlog struct */ crashlog = container_of(entry, struct crashlog_entry, entry); mutex_init(&crashlog->control_mutex); - crashlog->info = &crashlog_type1_ver0; + + crashlog->info = select_crashlog_info(type, version); header->access_type = GET_ACCESS(readl(disc_table)); header->guid = readl(disc_table + GUID_OFFSET); @@ -285,7 +521,7 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, /* Size is measured in DWORDS, but accessor returns bytes */ header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); - entry->attr_grp = &pmt_crashlog_group; + entry->attr_grp = crashlog->info->attr_grp; return 0; } -- GitLab From 232b41d3c2ce8cf4641a174416676458bf0de5b2 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 18 Jul 2025 18:33:04 +0200 Subject: [PATCH 826/868] platform/x86: oxpec: Fix turbo register for G1 AMD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out that the AMD variant of the G1 uses different EC registers than the Intel variant. Differentiate them and apply the correct ones to the AMD variant. Fixes: b369395c895b ("platform/x86: oxpec: Add support for the OneXPlayer G1") Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250718163305.159232-1-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 37 +++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 06759036945d4..9839e8cb82ce4 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -58,7 +58,8 @@ enum oxp_board { oxp_mini_amd_a07, oxp_mini_amd_pro, oxp_x1, - oxp_g1, + oxp_g1_i, + oxp_g1_a, }; static enum oxp_board board; @@ -247,14 +248,14 @@ static const struct dmi_system_id dmi_table[] = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 A"), }, - .driver_data = (void *)oxp_g1, + .driver_data = (void *)oxp_g1_a, }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER G1 i"), }, - .driver_data = (void *)oxp_g1, + .driver_data = (void *)oxp_g1_i, }, { .matches = { @@ -352,7 +353,8 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj, case oxp_mini_amd_a07: case oxp_mini_amd_pro: case oxp_x1: - case oxp_g1: + case oxp_g1_i: + case oxp_g1_a: return attr->mode; default: break; @@ -381,12 +383,13 @@ static ssize_t tt_toggle_store(struct device *dev, case aok_zoe_a1: case oxp_fly: case oxp_mini_amd_pro: + case oxp_g1_a: reg = OXP_TURBO_SWITCH_REG; mask = OXP_TURBO_TAKE_VAL; break; case oxp_2: case oxp_x1: - case oxp_g1: + case oxp_g1_i: reg = OXP_2_TURBO_SWITCH_REG; mask = OXP_TURBO_TAKE_VAL; break; @@ -426,12 +429,13 @@ static ssize_t tt_toggle_show(struct device *dev, case aok_zoe_a1: case oxp_fly: case oxp_mini_amd_pro: + case oxp_g1_a: reg = OXP_TURBO_SWITCH_REG; mask = OXP_TURBO_TAKE_VAL; break; case oxp_2: case oxp_x1: - case oxp_g1: + case oxp_g1_i: reg = OXP_2_TURBO_SWITCH_REG; mask = OXP_TURBO_TAKE_VAL; break; @@ -520,7 +524,8 @@ static bool oxp_psy_ext_supported(void) { switch (board) { case oxp_x1: - case oxp_g1: + case oxp_g1_i: + case oxp_g1_a: case oxp_fly: return true; default: @@ -659,7 +664,8 @@ static int oxp_pwm_enable(void) case oxp_mini_amd_a07: case oxp_mini_amd_pro: case oxp_x1: - case oxp_g1: + case oxp_g1_i: + case oxp_g1_a: return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); default: return -EINVAL; @@ -686,7 +692,8 @@ static int oxp_pwm_disable(void) case oxp_mini_amd_a07: case oxp_mini_amd_pro: case oxp_x1: - case oxp_g1: + case oxp_g1_i: + case oxp_g1_a: return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); default: return -EINVAL; @@ -713,7 +720,8 @@ static int oxp_pwm_read(long *val) case oxp_mini_amd_a07: case oxp_mini_amd_pro: case oxp_x1: - case oxp_g1: + case oxp_g1_i: + case oxp_g1_a: return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); default: return -EOPNOTSUPP; @@ -742,7 +750,7 @@ static int oxp_pwm_fan_speed(long *val) return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); case oxp_2: case oxp_x1: - case oxp_g1: + case oxp_g1_i: return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); case aok_zoe_a1: case aya_neo_2: @@ -757,6 +765,7 @@ static int oxp_pwm_fan_speed(long *val) case oxp_mini_amd: case oxp_mini_amd_a07: case oxp_mini_amd_pro: + case oxp_g1_a: return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); default: return -EOPNOTSUPP; @@ -776,7 +785,7 @@ static int oxp_pwm_input_write(long val) return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); case oxp_2: case oxp_x1: - case oxp_g1: + case oxp_g1_i: /* scale to range [0-184] */ val = (val * 184) / 255; return write_to_ec(OXP_SENSOR_PWM_REG, val); @@ -796,6 +805,7 @@ static int oxp_pwm_input_write(long val) case aok_zoe_a1: case oxp_fly: case oxp_mini_amd_pro: + case oxp_g1_a: return write_to_ec(OXP_SENSOR_PWM_REG, val); default: return -EOPNOTSUPP; @@ -816,7 +826,7 @@ static int oxp_pwm_input_read(long *val) break; case oxp_2: case oxp_x1: - case oxp_g1: + case oxp_g1_i: ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); if (ret) return ret; @@ -842,6 +852,7 @@ static int oxp_pwm_input_read(long *val) case aok_zoe_a1: case oxp_fly: case oxp_mini_amd_pro: + case oxp_g1_a: default: ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); if (ret) -- GitLab From 1798561befd8be1e52feb54f850efcab5a595f43 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 18 Jul 2025 18:33:05 +0200 Subject: [PATCH 827/868] platform/x86: oxpec: Add support for OneXPlayer X1 Mini Pro (Strix Point) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OneXPlayer X1 Mini Pro (which is the Strix Point variant of the Mini) uses the same registers as the X1 Mini, so re-use the quirk. Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250718163305.159232-2-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 9839e8cb82ce4..eb076bb4099be 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -292,6 +292,13 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_x1, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Mini Pro"), + }, + .driver_data = (void *)oxp_x1, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), -- GitLab From f9db1fc56281b96fe8748632b3894de970a8a850 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 21 Jul 2025 16:37:14 -0500 Subject: [PATCH 828/868] ACPI: Fix typos Fix typos in documentation and comments. Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/20250722132653.GA2781885@bhelgaas Signed-off-by: Rafael J. Wysocki --- Documentation/ABI/testing/sysfs-firmware-acpi | 6 +++--- Documentation/firmware-guide/acpi/gpio-properties.rst | 4 ++-- drivers/acpi/bus.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index f4de60c4134d4..72e7c9161ce7a 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -108,15 +108,15 @@ Description: number of a "General Purpose Events" (GPE). A GPE vectors to a specified handler in AML, which - can do a anything the BIOS writer wants from + can do anything the BIOS writer wants from OS context. GPE 0x12, for example, would vector to a level or edge handler called _L12 or _E12. The handler may do its business and return. - Or the handler may send send a Notify event + Or the handler may send a Notify event to a Linux device driver registered on an ACPI device, such as a battery, or a processor. - To figure out where all the SCI's are coming from, + To figure out where all the SCIs are coming from, /sys/firmware/acpi/interrupts contains a file listing every possible source, and the count of how many times it has triggered:: diff --git a/Documentation/firmware-guide/acpi/gpio-properties.rst b/Documentation/firmware-guide/acpi/gpio-properties.rst index db0c0b1f37008..1e603189b5b14 100644 --- a/Documentation/firmware-guide/acpi/gpio-properties.rst +++ b/Documentation/firmware-guide/acpi/gpio-properties.rst @@ -92,8 +92,8 @@ and polarity settings. The table below shows the expectations: | | Low | as low, assuming active | +-------------+-------------+-----------------------------------------------+ -That said, for our above example the both GPIOs, since the bias setting -is explicit and _DSD is present, will be treated as active with a high +That said, for our above example, since the bias setting is explicit and +_DSD is present, both GPIOs will be treated as active with a high polarity and Linux will configure the pins in this state until a driver reprograms them differently. diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c2ab2783303f2..a984ccd4a2a0c 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1406,7 +1406,7 @@ static int __init acpi_bus_init(void) goto error1; /* - * Register the for all standard device notifications. + * Register for all standard device notifications. */ status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, -- GitLab From e2374953461947eee49f69b3e3204ff080ef31b1 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 22 Jul 2025 12:05:13 +0000 Subject: [PATCH 829/868] platform/chrome: cros_ec: Unregister notifier in cros_ec_unregister() The blocking notifier is registered in cros_ec_register(); however, it isn't unregistered in cros_ec_unregister(). Fix it. Fixes: 42cd0ab476e2 ("platform/chrome: cros_ec: Query EC protocol version if EC transitions between RO/RW") Cc: stable@vger.kernel.org Reviewed-by: Benson Leung Link: https://lore.kernel.org/r/20250722120513.234031-1-tzungbi@kernel.org Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index 110771a8645ed..fd58781a2fb7d 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -318,6 +318,9 @@ EXPORT_SYMBOL(cros_ec_register); */ void cros_ec_unregister(struct cros_ec_device *ec_dev) { + if (ec_dev->mkbp_event_supported) + blocking_notifier_chain_unregister(&ec_dev->event_notifier, + &ec_dev->notifier_ready); platform_device_unregister(ec_dev->pd); platform_device_unregister(ec_dev->ec); mutex_destroy(&ec_dev->lock); -- GitLab From 731a4702b668ef28730e7d2414672b7085e757d6 Mon Sep 17 00:00:00 2001 From: Tomasz Michalec Date: Tue, 22 Jul 2025 15:28:26 +0200 Subject: [PATCH 830/868] platform/chrome: cros_ec_typec: Check ec platform device pointer It is possible that parent device for cros_ec_typec device is already available, but ec pointer in parent driver data isn't populated yet. It may happen when cros_typec_probe is running in parallel with cros_ec_register. This leads to NULL pointer dereference when cros_typec_probe tries to get driver data from typec->ec->ec->dev. Check if typec->ec->ec is set before using it in cros_typec_probe. Signed-off-by: Tomasz Michalec Link: https://lore.kernel.org/r/20250722132826.707087-1-tmichalec@google.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_typec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 5a141401e4857..b712bcff6fb26 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -1354,7 +1354,7 @@ static int cros_typec_probe(struct platform_device *pdev) typec->dev = dev; typec->ec = dev_get_drvdata(pdev->dev.parent); - if (!typec->ec) { + if (!typec->ec || !typec->ec->ec) { dev_warn(dev, "couldn't find parent EC device\n"); return -EPROBE_DEFER; } -- GitLab From 8206650c604687687bed5898b3bdb90e5d361ed4 Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Tue, 22 Jul 2025 13:16:08 -0500 Subject: [PATCH 831/868] gpio: tps65219: Update _IDX & _OFFSET macro prefix TPS65215 and TPS65219 are overlapping PMIC devices. While their regulator features differe, the GPIO features are the same. In the TPS65219 MFD driver, the 2 PMICs share the same "tps65219-gpio" compatible string to limit support for TPS65215 in this GPIO driver to comments. The TPS6521X_GPIO0_IDX and TPS6521X_GPIO0_OFFSET macro name prefixes are updated to indicate these macros apply to both PMICs. Reviewed-by: Roger Quadros Acked-by: Linus Walleij Reviewed-by: Jonathan Cormier Signed-off-by: Shree Ramamoorthy Link: https://lore.kernel.org/r/20250722181609.1541739-2-s-ramamoorthy@ti.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65219.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index 630cb41e77a4e..c8f8f88b4239b 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * GPIO driver for TI TPS65219 PMICs + * GPIO driver for TI TPS65215/TPS65219 PMICs * - * Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2022, 2025 Texas Instruments Incorporated - http://www.ti.com/ */ #include @@ -13,8 +13,15 @@ #include #define TPS65219_GPIO0_DIR_MASK BIT(3) -#define TPS65219_GPIO0_OFFSET 2 -#define TPS65219_GPIO0_IDX 0 +#define TPS6521X_GPIO0_OFFSET 2 +#define TPS6521X_GPIO0_IDX 0 + +/* + * TPS65215 & TPS65219 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin8 ) -> bit_offset 0 + * Linux gpio offset 2 -> GPO2 (pin17) -> bit_offset 1 + */ struct tps65219_gpio { struct gpio_chip gpio_chip; @@ -26,7 +33,7 @@ static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset struct tps65219_gpio *gpio = gpiochip_get_data(gc); int ret, val; - if (offset != TPS65219_GPIO0_IDX) + if (offset != TPS6521X_GPIO0_IDX) return GPIO_LINE_DIRECTION_OUT; ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); @@ -42,7 +49,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset) struct device *dev = gpio->tps->dev; int ret, val; - if (offset != TPS65219_GPIO0_IDX) { + if (offset != TPS6521X_GPIO0_IDX) { dev_err(dev, "GPIO%d is output only, cannot get\n", offset); return -ENOTSUPP; } @@ -70,7 +77,7 @@ static int tps65219_gpio_set(struct gpio_chip *gc, unsigned int offset, int valu struct tps65219_gpio *gpio = gpiochip_get_data(gc); int v, mask, bit; - bit = (offset == TPS65219_GPIO0_IDX) ? TPS65219_GPIO0_OFFSET : offset - 1; + bit = (offset == TPS6521X_GPIO0_IDX) ? TPS6521X_GPIO0_OFFSET : offset - 1; mask = BIT(bit); v = value ? mask : 0; @@ -116,7 +123,7 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs struct tps65219_gpio *gpio = gpiochip_get_data(gc); struct device *dev = gpio->tps->dev; - if (offset != TPS65219_GPIO0_IDX) { + if (offset != TPS6521X_GPIO0_IDX) { dev_err(dev, "GPIO%d is output only, cannot change to input\n", offset); return -ENOTSUPP; } @@ -130,7 +137,7 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) { tps65219_gpio_set(gc, offset, value); - if (offset != TPS65219_GPIO0_IDX) + if (offset != TPS6521X_GPIO0_IDX) return 0; if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) @@ -178,5 +185,5 @@ module_platform_driver(tps65219_gpio_driver); MODULE_ALIAS("platform:tps65219-gpio"); MODULE_AUTHOR("Jonathan Cormier "); -MODULE_DESCRIPTION("TPS65219 GPIO driver"); +MODULE_DESCRIPTION("TPS65215/TPS65219 GPIO driver"); MODULE_LICENSE("GPL"); -- GitLab From 1b6ab07c0c800ed32ce417b71b32bb1baa91b493 Mon Sep 17 00:00:00 2001 From: Shree Ramamoorthy Date: Tue, 22 Jul 2025 13:16:09 -0500 Subject: [PATCH 832/868] gpio: tps65219: Add support for TI TPS65214 PMIC Add support for the TI TPS65214 PMIC with the addition of an id_table, separate TPS65214 template_chip, and device-specific _change_direction functions. - Use platform_get_device_id() to assign dev-specific information. - Use different change_direction() functions since TPS65214's GPIO configuration bits are changeable during device operation through bit GPIO_CONFIG in GENERAL_CONFIG register. - Remove MODULE_ALIAS since it is now generated by MODULE_DEVICE_TABLE. Reviewed-by: Jonathan Cormier Tested-by: Jonathan Cormier Signed-off-by: Shree Ramamoorthy Link: https://lore.kernel.org/r/20250722181609.1541739-3-s-ramamoorthy@ti.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tps65219.c | 92 +++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-tps65219.c b/drivers/gpio/gpio-tps65219.c index c8f8f88b4239b..c0177088c54cb 100644 --- a/drivers/gpio/gpio-tps65219.c +++ b/drivers/gpio/gpio-tps65219.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * GPIO driver for TI TPS65215/TPS65219 PMICs + * GPIO driver for TI TPS65214/TPS65215/TPS65219 PMICs * * Copyright (C) 2022, 2025 Texas Instruments Incorporated - http://www.ti.com/ */ @@ -13,10 +13,15 @@ #include #define TPS65219_GPIO0_DIR_MASK BIT(3) +#define TPS65214_GPIO0_DIR_MASK BIT(1) #define TPS6521X_GPIO0_OFFSET 2 #define TPS6521X_GPIO0_IDX 0 /* + * TPS65214 GPIO mapping + * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 + * Linux gpio offset 1 -> GPO1 (pin9 ) -> bit_offset 0 + * * TPS65215 & TPS65219 GPIO mapping * Linux gpio offset 0 -> GPIO (pin16) -> bit_offset 2 * Linux gpio offset 1 -> GPO1 (pin8 ) -> bit_offset 0 @@ -24,10 +29,26 @@ */ struct tps65219_gpio { + int (*change_dir)(struct gpio_chip *gc, unsigned int offset, unsigned int dir); struct gpio_chip gpio_chip; struct tps65219 *tps; }; +static int tps65214_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + int ret, val; + + if (offset != TPS6521X_GPIO0_IDX) + return GPIO_LINE_DIRECTION_OUT; + + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, &val); + if (ret) + return ret; + + return !(val & TPS65214_GPIO0_DIR_MASK); +} + static int tps65219_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); @@ -118,6 +139,33 @@ static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int off return -ENOTSUPP; } +static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int offset, + unsigned int direction) +{ + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + struct device *dev = gpio->tps->dev; + int val, ret; + + /** + * Verified if GPIO or GPO in parent function + * Masked value: 0 = GPIO, 1 = VSEL + */ + ret = regmap_read(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG, &val); + if (ret) + return ret; + + ret = !!(val & BIT(TPS65219_GPIO0_DIR_MASK)); + if (ret) + dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset); + + ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG, + TPS65214_GPIO0_DIR_MASK, direction); + if (ret) + dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset); + + return ret; +} + static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) { struct tps65219_gpio *gpio = gpiochip_get_data(gc); @@ -131,11 +179,13 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN) return 0; - return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_IN); + return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN); } static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) { + struct tps65219_gpio *gpio = gpiochip_get_data(gc); + tps65219_gpio_set(gc, offset, value); if (offset != TPS6521X_GPIO0_IDX) return 0; @@ -143,9 +193,22 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT) return 0; - return tps65219_gpio_change_direction(gc, offset, GPIO_LINE_DIRECTION_OUT); + return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT); } +static const struct gpio_chip tps65214_template_chip = { + .label = "tps65214-gpio", + .owner = THIS_MODULE, + .get_direction = tps65214_gpio_get_direction, + .direction_input = tps65219_gpio_direction_input, + .direction_output = tps65219_gpio_direction_output, + .get = tps65219_gpio_get, + .set_rv = tps65219_gpio_set, + .base = -1, + .ngpio = 2, + .can_sleep = true, +}; + static const struct gpio_chip tps65219_template_chip = { .label = "tps65219-gpio", .owner = THIS_MODULE, @@ -161,6 +224,7 @@ static const struct gpio_chip tps65219_template_chip = { static int tps65219_gpio_probe(struct platform_device *pdev) { + enum pmic_id chip = platform_get_device_id(pdev)->driver_data; struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); struct tps65219_gpio *gpio; @@ -168,22 +232,38 @@ static int tps65219_gpio_probe(struct platform_device *pdev) if (!gpio) return -ENOMEM; + if (chip == TPS65214) { + gpio->gpio_chip = tps65214_template_chip; + gpio->change_dir = tps65214_gpio_change_direction; + } else if (chip == TPS65219) { + gpio->gpio_chip = tps65219_template_chip; + gpio->change_dir = tps65219_gpio_change_direction; + } else { + return -ENODATA; + } + gpio->tps = tps; - gpio->gpio_chip = tps65219_template_chip; gpio->gpio_chip.parent = tps->dev; return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio_chip, gpio); } +static const struct platform_device_id tps6521x_gpio_id_table[] = { + { "tps65214-gpio", TPS65214 }, + { "tps65219-gpio", TPS65219 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table); + static struct platform_driver tps65219_gpio_driver = { .driver = { .name = "tps65219-gpio", }, .probe = tps65219_gpio_probe, + .id_table = tps6521x_gpio_id_table, }; module_platform_driver(tps65219_gpio_driver); -MODULE_ALIAS("platform:tps65219-gpio"); MODULE_AUTHOR("Jonathan Cormier "); -MODULE_DESCRIPTION("TPS65215/TPS65219 GPIO driver"); +MODULE_DESCRIPTION("TPS65214/TPS65215/TPS65219 GPIO driver"); MODULE_LICENSE("GPL"); -- GitLab From ff4322b22f35c010643de16c6f13e285bf314d83 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 23 Jul 2025 14:46:08 +0800 Subject: [PATCH 833/868] gpio: cadence: Remove duplicated include in gpio-cadence.c The header files linux/gpio/driver.h is included twice in gpio-cadence.c, so one inclusion of each can be removed. Reported-by: Abaci Robot Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=22931 Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20250723064608.2178024-1-yang.lee@linux.alibaba.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-cadence.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index 8243eddcd5bbe..c647953521c71 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include -- GitLab From 1f590fa4b93dd7c7daaa4e09d8381ac2aab3853c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 11 Jul 2025 18:32:52 +0200 Subject: [PATCH 834/868] spi: spi-qpic-snand: simplify bad block marker duplication Due to the expectations of the SPINAND code, the driver duplicates the bad block markers during raw OOB reads. It has been implemented by using two if statements, and due to the opposite conditions one of conditional codepaths always runs. Since the effect of both codepaths is the same, remove the if statements and use a single line solution instead. Also add a note about why the duplication is required. No functional changes intended. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250711-qpic-snand-simplify-bbm-copy-v1-1-dd2608325f72@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index c49bf7079808a..0cfa0d960fd3c 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -613,10 +613,16 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); - if (snandc->data_buffer[bbpos] == 0xff) - snandc->data_buffer[bbpos + 1] = 0xff; - if (snandc->data_buffer[bbpos] != 0xff) - snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; + /* + * TODO: The SPINAND code expects two bad block marker bytes + * at the beginning of the OOB area, but the OOB layout used by + * the driver has only one. Duplicate that for now in order to + * avoid certain blocks to be marked as bad. + * + * This can be removed once single-byte bad block marker support + * gets implemented in the SPINAND code. + */ + snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos]; memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes); -- GitLab From b102c9d89fecd72be83eaab9b384285e2d0dc940 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 23 Jul 2025 16:03:38 +0200 Subject: [PATCH 835/868] ASoC: dt-bindings: qcom,q6afe: Document q6usb subnode Document the subnode for Q6USB, used for USB audio offloading. Cc: Wesley Cheng Acked-by: Rob Herring (Arm) Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250723-fp4-usb-audio-offload-v3-1-6be84ed4fc39@fairphone.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,q6afe.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml index 297aa362aa54a..268f7073d7972 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml @@ -29,6 +29,12 @@ properties: unevaluatedProperties: false description: Qualcomm DSP audio ports + usbd: + type: object + $ref: /schemas/sound/qcom,q6usb.yaml# + unevaluatedProperties: false + description: Qualcomm DSP USB audio ports + required: - compatible - dais @@ -64,5 +70,12 @@ examples: qcom,sd-lines = <0 1 2 3>; }; }; + + usbd { + compatible = "qcom,q6usb"; + #sound-dai-cells = <1>; + iommus = <&apps_smmu 0x180f 0x0>; + qcom,usb-audio-intr-idx = /bits/ 16 <2>; + }; }; }; -- GitLab From d664e75317e19bb79b6d207f7729e35eca504a6a Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 23 Jul 2025 16:03:39 +0200 Subject: [PATCH 836/868] ASoC: dt-bindings: qcom,sm8250: Add Fairphone 4 sound card Document the bindings for the sound card on Fairphone 4 which uses the older non-audioreach audio architecture. Acked-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250723-fp4-usb-audio-offload-v3-2-6be84ed4fc39@fairphone.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 6b4a8dbdaf615..5d3dbb6cb1ae8 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -28,6 +28,7 @@ properties: - qcom,sm8750-sndcard - const: qcom,sm8450-sndcard - enum: + - fairphone,fp4-sndcard - fairphone,fp5-sndcard - qcom,apq8096-sndcard - qcom,qcm6490-idp-sndcard -- GitLab From c58c35ef6ae62e36927f506a5afc66610b7261d9 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Wed, 23 Jul 2025 16:03:40 +0200 Subject: [PATCH 837/868] ASoC: qcom: sm8250: Add Fairphone 4 soundcard compatible Add a compatible for the SM7225-based Fairphone 4 which can use this machine driver. Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250723-fp4-usb-audio-offload-v3-3-6be84ed4fc39@fairphone.com Signed-off-by: Mark Brown --- sound/soc/qcom/sm8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 69c514fad0b1b..f5b75a06e5bd2 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -208,6 +208,7 @@ static int sm8250_platform_probe(struct platform_device *pdev) } static const struct of_device_id snd_sm8250_dt_match[] = { + { .compatible = "fairphone,fp4-sndcard", .data = "sm7225" }, { .compatible = "fairphone,fp5-sndcard", .data = "qcm6490" }, { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" }, { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" }, -- GitLab From ab29b3460c5cec24bde75278d7ffca23cac1b867 Mon Sep 17 00:00:00 2001 From: Baojun Xu Date: Wed, 23 Jul 2025 22:24:23 +0800 Subject: [PATCH 838/868] ALSA: hda: Add TAS2770 support Add TAS2770 support in TI's HDA driver. And add hda_chip_id for more products. Distinguish DSP and non-DSP in firmware loading function. Signed-off-by: Baojun Xu Link: https://patch.msgid.link/20250723142423.38768-1-baojun.xu@ti.com Signed-off-by: Takashi Iwai --- include/sound/tas2770-tlv.h | 23 +++ .../hda/codecs/side-codecs/tas2781_hda_i2c.c | 157 ++++++++++++------ 2 files changed, 125 insertions(+), 55 deletions(-) create mode 100644 include/sound/tas2770-tlv.h diff --git a/include/sound/tas2770-tlv.h b/include/sound/tas2770-tlv.h new file mode 100644 index 0000000000000..c0bd495b4a07f --- /dev/null +++ b/include/sound/tas2770-tlv.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// ALSA SoC Texas Instruments TAS2770 Audio Smart Amplifier +// +// Copyright (C) 2025 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2770 hda driver implements for one, two, or even multiple +// TAS2770 chips. +// +// Author: Baojun Xu +// + +#ifndef __TAS2770_TLV_H__ +#define __TAS2770_TLV_H__ + +#define TAS2770_DVC_LEVEL TASDEVICE_REG(0x0, 0x0, 0x17) +#define TAS2770_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) + +static const __maybe_unused DECLARE_TLV_DB_SCALE(tas2770_dvc_tlv, 1650, 50, 0); +static const __maybe_unused DECLARE_TLV_DB_SCALE(tas2770_amp_tlv, 1100, 50, 0); + +#endif diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index bacc3f6ed4bd9..a0b1326818041 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "hda_local.h" @@ -45,9 +46,18 @@ #define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14) #define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34) +enum device_chip_id { + HDA_TAS2563, + HDA_TAS2770, + HDA_TAS2781, + HDA_OTHERS +}; + struct tas2781_hda_i2c_priv { struct snd_kcontrol *snd_ctls[2]; int (*save_calibration)(struct tas2781_hda *h); + + int hda_chip_id; }; static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) @@ -245,6 +255,15 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; } +static const struct snd_kcontrol_new tas2770_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2770_AMP_LEVEL, + 0, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_amp_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2770_DVC_LEVEL, + 0, 0, 31, 0, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_dvc_tlv), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, @@ -253,7 +272,7 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { tas2781_force_fwload_get, tas2781_force_fwload_put), }; -static const struct snd_kcontrol_new tas2781_prof_ctrl = { +static const struct snd_kcontrol_new tasdevice_prof_ctrl = { .name = "Speaker Profile Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_profile, @@ -261,7 +280,7 @@ static const struct snd_kcontrol_new tas2781_prof_ctrl = { .put = tasdevice_set_profile_id, }; -static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_prog_ctrl = { .name = "Speaker Program Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_programs, @@ -269,7 +288,7 @@ static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { .put = tasdevice_program_put, }; -static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_conf_ctrl = { .name = "Speaker Config Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_config, @@ -378,44 +397,34 @@ static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) snd_ctl_remove(codec->card, tas_hda->prof_ctl); } -static void tasdev_fw_ready(const struct firmware *fmw, void *context) +static void tasdev_add_kcontrols(struct tasdevice_priv *tas_priv, + struct snd_kcontrol **ctls, struct hda_codec *codec, + const struct snd_kcontrol_new *tas_snd_ctrls, int num_ctls) { - struct tasdevice_priv *tas_priv = 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 i, ret, spk_id; - - pm_runtime_get_sync(tas_priv->dev); - mutex_lock(&tas_priv->codec_lock); + int i, ret; - ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) - goto out; - - tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl, tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_prof_ctrl.name, ret); - goto out; - } - - for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) { - hda_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i], - tas_priv); - ret = snd_ctl_add(codec->card, hda_priv->snd_ctls[i]); + for (i = 0; i < num_ctls; i++) { + ctls[i] = snd_ctl_new1( + &tas_snd_ctrls[i], tas_priv); + ret = snd_ctl_add(codec->card, ctls[i]); if (ret) { dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", - tas2781_snd_controls[i].name, ret); - goto out; + tas_snd_ctrls[i].name, ret); + break; } } +} - tasdevice_dsp_remove(tas_priv); +static void tasdevice_dspfw_init(void *context) +{ + struct tasdevice_priv *tas_priv = 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; + 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. @@ -441,28 +450,12 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; - goto out; - } - - tas_hda->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_prog_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_prog_ctrl.name, ret); - goto out; - } - - tas_hda->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_conf_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_conf_ctrl.name, ret); - goto out; + return; } + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_prog_ctl, codec, + &tasdevice_dsp_prog_ctrl, 1); + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_conf_ctl, codec, + &tasdevice_dsp_conf_ctrl, 1); tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; tasdevice_prmg_load(tas_priv, 0); @@ -475,9 +468,45 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) * calibrated data inside algo. */ hda_priv->save_calibration(tas_hda); +} + +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = 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; + + pm_runtime_get_sync(tas_priv->dev); + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; - tasdevice_tuning_switch(tas_hda->priv, 0); - tas_hda->priv->playback_started = true; + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; + tasdev_add_kcontrols(tas_priv, &tas_hda->prof_ctl, codec, + &tasdevice_prof_ctrl, 1); + + switch (hda_priv->hda_chip_id) { + case HDA_TAS2770: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2770_snd_controls[0], + ARRAY_SIZE(tas2770_snd_controls)); + break; + case HDA_TAS2781: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2781_snd_controls[0], + ARRAY_SIZE(tas2781_snd_controls)); + tasdevice_dspfw_init(context); + break; + case HDA_TAS2563: + tasdevice_dspfw_init(context); + break; + default: + break; + } out: mutex_unlock(&tas_hda->priv->codec_lock); @@ -581,16 +610,33 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) return -ENOMEM; if (strstr(dev_name(&clt->dev), "TIAS2781")) { + /* + * TAS2781, integrated on-chip DSP with + * global I2C address supported. + */ device_name = "TIAS2781"; + hda_priv->hda_chip_id = HDA_TAS2781; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; + } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) { + /* + * TAS2770, has no on-chip DSP, so no calibration data + * required; has no global I2C address supported. + */ + device_name = "TXNW2770"; + hda_priv->hda_chip_id = HDA_TAS2770; } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2781:00-tas2781-hda.0")) { device_name = "TXNW2781"; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; } else if (strstr(dev_name(&clt->dev), "INT8866")) { + /* + * TAS2563, integrated on-chip DSP with + * global I2C address supported. + */ device_name = "INT8866"; + hda_priv->hda_chip_id = HDA_TAS2563; hda_priv->save_calibration = tas2563_save_calibration; tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR; } else { @@ -727,6 +773,7 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = { static const struct acpi_device_id tas2781_acpi_hda_match[] = { {"INT8866", 0 }, {"TIAS2781", 0 }, + {"TXNW2770", 0 }, {"TXNW2781", 0 }, {} }; -- GitLab From cc2d5b72b13b3af2b9b4bed3d5dfd0de14414230 Mon Sep 17 00:00:00 2001 From: Keenan Salandy Date: Wed, 23 Jul 2025 10:09:30 -0400 Subject: [PATCH 839/868] platform/chrome: Fix typo in CROS_USBPD_NOTIFY help text Correct the misspelling "platorms" to "platforms" in the help text for the CROS_USBPD_NOTIFY Kconfig option. Signed-off-by: Keenan Salandy Link: https://lore.kernel.org/r/20250723140930.1443-1-keenansalandy@gmail.com Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 10941ac373058..2281d6dacc9bc 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -286,7 +286,7 @@ config CROS_USBPD_NOTIFY default MFD_CROS_EC_DEV help If you say Y here, you get support for Type-C PD event notifications - from the ChromeOS EC. On ACPI platorms this driver will bind to the + from the ChromeOS EC. On ACPI platforms this driver will bind to the GOOG0003 ACPI device, and on platforms which don't have this device it will get initialized on ECs which support the feature EC_FEATURE_USB_PD. -- GitLab From 28517c8b6275a9cd25a4974d0e4d58eaba465a67 Mon Sep 17 00:00:00 2001 From: Dimitri Fedrau Date: Wed, 23 Jul 2025 19:34:56 +0200 Subject: [PATCH 840/868] pwm: mc33xs2410: add hwmon support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for hwmon is provided by a separate driver residing in hwmon subsystem which is implemented as auxiliary device. Add handling of this device. Signed-off-by: Dimitri Fedrau Link: https://lore.kernel.org/r/20250723-mc33xs2410-hwmon-v5-1-f62aab71cd59@liebherr.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 1 + drivers/pwm/pwm-mc33xs2410.c | 20 ++++++++++++++++++-- include/linux/mc33xs2410.h | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 include/linux/mc33xs2410.h diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3ef1757502ebd..64f1c86340fdc 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -436,6 +436,7 @@ config PWM_MC33XS2410 tristate "MC33XS2410 PWM support" depends on OF depends on SPI + select AUXILIARY_BUS help NXP MC33XS2410 high-side switch driver. The MC33XS2410 is a four channel high-side switch. The device is operational from 3.0 V diff --git a/drivers/pwm/pwm-mc33xs2410.c b/drivers/pwm/pwm-mc33xs2410.c index a1ac3445ccdb4..6d99e3ff72398 100644 --- a/drivers/pwm/pwm-mc33xs2410.c +++ b/drivers/pwm/pwm-mc33xs2410.c @@ -17,11 +17,14 @@ * behavior of the output pin that is neither the old nor the new state, * rather something in between. */ +#define DEFAULT_SYMBOL_NAMESPACE "PWM_MC33XS2410" +#include #include #include #include #include +#include #include #include #include @@ -120,12 +123,19 @@ static int mc33xs2410_read_reg(struct spi_device *spi, u8 reg, u16 *val, u8 flag return mc33xs2410_read_regs(spi, ®, flag, val, 1); } -static int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) +int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) { return mc33xs2410_read_reg(spi, reg, val, MC33XS2410_FRAME_IN_DATA_RD); } +EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_ctrl); -static int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) +int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val) +{ + return mc33xs2410_read_reg(spi, reg, val, 0); +} +EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_diag); + +int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) { u16 tmp; int ret; @@ -139,6 +149,7 @@ static int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val return mc33xs2410_write_reg(spi, reg, tmp); } +EXPORT_SYMBOL_GPL(mc33xs2410_modify_reg); static u8 mc33xs2410_pwm_get_freq(u64 period) { @@ -314,6 +325,7 @@ static int mc33xs2410_reset(struct device *dev) static int mc33xs2410_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + struct auxiliary_device *adev; struct pwm_chip *chip; int ret; @@ -361,6 +373,10 @@ static int mc33xs2410_probe(struct spi_device *spi) if (ret < 0) return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); + adev = devm_auxiliary_device_create(dev, "hwmon", NULL); + if (!adev) + return dev_err_probe(dev, -ENODEV, "Failed to register hwmon device\n"); + return 0; } diff --git a/include/linux/mc33xs2410.h b/include/linux/mc33xs2410.h new file mode 100644 index 0000000000000..31c0edf10dd73 --- /dev/null +++ b/include/linux/mc33xs2410.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH + */ +#ifndef _MC33XS2410_H +#define _MC33XS2410_H + +#include + +MODULE_IMPORT_NS("PWM_MC33XS2410"); + +int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val); +int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val); +int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val); + +#endif /* _MC33XS2410_H */ -- GitLab From a02b105fe9f2b82cbd13b13a98c2b9ffae4a7c27 Mon Sep 17 00:00:00 2001 From: Dimitri Fedrau Date: Wed, 23 Jul 2025 19:34:57 +0200 Subject: [PATCH 841/868] hwmon: add support for MC33XS2410 hardware monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device is able to monitor temperature, voltage and current of each of the four outputs. Add basic support for monitoring the temperature of the four outputs and the die temperature. Signed-off-by: Dimitri Fedrau Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20250723-mc33xs2410-hwmon-v5-2-f62aab71cd59@liebherr.com Signed-off-by: Uwe Kleine-König --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/mc33xs2410_hwmon.rst | 34 +++++ drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/mc33xs2410_hwmon.c | 178 +++++++++++++++++++++++ 5 files changed, 224 insertions(+) create mode 100644 Documentation/hwmon/mc33xs2410_hwmon.rst create mode 100644 drivers/hwmon/mc33xs2410_hwmon.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index b45bfb4ebf308..d292a86ac5da9 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -167,6 +167,7 @@ Hardware Monitoring Kernel Drivers max77705 max8688 mc13783-adc + mc33xs2410_hwmon mc34vr500 mcp3021 menf21bmc diff --git a/Documentation/hwmon/mc33xs2410_hwmon.rst b/Documentation/hwmon/mc33xs2410_hwmon.rst new file mode 100644 index 0000000000000..8a2136ef91391 --- /dev/null +++ b/Documentation/hwmon/mc33xs2410_hwmon.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mc33xs2410_hwmon +============================== + +Supported devices: + + * NXPs MC33XS2410 + + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf + +Authors: + + Dimitri Fedrau + +Description +----------- + +The MC33XS2410 is a four channel self-protected high-side switch featuring +hardware monitoring functions such as temperature, current and voltages for each +of the four channels. + +Sysfs entries +------------- + +======================= ====================================================== +temp1_label "Central die temperature" +temp1_input Measured temperature of central die + +temp[2-5]_label "Channel [1-4] temperature" +temp[2-5]_input Measured temperature of a single channel +temp[2-5]_alarm Temperature alarm +temp[2-5]_max Maximal temperature +======================= ====================================================== diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 079620dd42862..9d28fcf7cd2a6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -700,6 +700,16 @@ config SENSORS_MC13783_ADC help Support for the A/D converter on MC13783 and MC13892 PMIC. +config SENSORS_MC33XS2410 + tristate "MC33XS2410 HWMON support" + depends on PWM_MC33XS2410 + help + If you say yes here you get hardware monitoring support for + MC33XS2410. + + This driver can also be built as a module. If so, the module + will be called mc33xs2410_hwmon. + config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on (X86 || COMPILE_TEST) && I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 48e5866c0c9a7..cd8bc4752b4db 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_MAX31827) += max31827.o obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_TC654) += tc654.o diff --git a/drivers/hwmon/mc33xs2410_hwmon.c b/drivers/hwmon/mc33xs2410_hwmon.c new file mode 100644 index 0000000000000..23eb90e337091 --- /dev/null +++ b/drivers/hwmon/mc33xs2410_hwmon.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH + */ + +#include +#include +#include +#include +#include +#include + +/* ctrl registers */ + +#define MC33XS2410_TEMP_WT 0x29 +#define MC33XS2410_TEMP_WT_MASK GENMASK(7, 0) + +/* diag registers */ + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_OUT_STA(chan) (0x02 + (chan) - 1) +#define MC33XS2410_OUT_STA_OTW BIT(8) + +#define MC33XS2410_TS_TEMP_DIE 0x26 +#define MC33XS2410_TS_TEMP_MASK GENMASK(9, 0) + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_TS_TEMP(chan) (0x2f + (chan) - 1) + +static const struct hwmon_channel_info * const mc33xs2410_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_LABEL | HWMON_T_INPUT, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM), + NULL, +}; + +static umode_t mc33xs2410_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_alarm: + case hwmon_temp_label: + return 0444; + case hwmon_temp_max: + return 0644; + default: + return 0; + } +} + +static int mc33xs2410_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u16 reg_val; + int ret; + u8 reg; + + switch (attr) { + case hwmon_temp_input: + reg = (channel == 0) ? MC33XS2410_TS_TEMP_DIE : + MC33XS2410_TS_TEMP(channel); + ret = mc33xs2410_read_reg_diag(spi, reg, ®_val); + if (ret < 0) + return ret; + + /* LSB is 0.25 degree celsius */ + *val = FIELD_GET(MC33XS2410_TS_TEMP_MASK, reg_val) * 250 - 40000; + return 0; + case hwmon_temp_alarm: + ret = mc33xs2410_read_reg_diag(spi, MC33XS2410_OUT_STA(channel), + ®_val); + if (ret < 0) + return ret; + + *val = FIELD_GET(MC33XS2410_OUT_STA_OTW, reg_val); + return 0; + case hwmon_temp_max: + ret = mc33xs2410_read_reg_ctrl(spi, MC33XS2410_TEMP_WT, ®_val); + if (ret < 0) + return ret; + + /* LSB is 1 degree celsius */ + *val = FIELD_GET(MC33XS2410_TEMP_WT_MASK, reg_val) * 1000 - 40000; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int mc33xs2410_hwmon_write(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct spi_device *spi = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_max: + val = clamp_val(val, -40000, 215000); + + /* LSB is 1 degree celsius */ + val = (val / 1000) + 40; + return mc33xs2410_modify_reg(spi, MC33XS2410_TEMP_WT, + MC33XS2410_TEMP_WT_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static const char *const mc33xs2410_temp_label[] = { + "Central die temperature", + "Channel 1 temperature", + "Channel 2 temperature", + "Channel 3 temperature", + "Channel 4 temperature", +}; + +static int mc33xs2410_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + *str = mc33xs2410_temp_label[channel]; + + return 0; +} + +static const struct hwmon_ops mc33xs2410_hwmon_hwmon_ops = { + .is_visible = mc33xs2410_hwmon_is_visible, + .read = mc33xs2410_hwmon_read, + .read_string = mc33xs2410_read_string, + .write = mc33xs2410_hwmon_write, +}; + +static const struct hwmon_chip_info mc33xs2410_hwmon_chip_info = { + .ops = &mc33xs2410_hwmon_hwmon_ops, + .info = mc33xs2410_hwmon_info, +}; + +static int mc33xs2410_hwmon_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct spi_device *spi = container_of(dev->parent, struct spi_device, dev); + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, NULL, spi, + &mc33xs2410_hwmon_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon); +} + +static const struct auxiliary_device_id mc33xs2410_hwmon_ids[] = { + { + .name = "pwm_mc33xs2410.hwmon", + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, mc33xs2410_hwmon_ids); + +static struct auxiliary_driver mc33xs2410_hwmon_driver = { + .probe = mc33xs2410_hwmon_probe, + .id_table = mc33xs2410_hwmon_ids, +}; +module_auxiliary_driver(mc33xs2410_hwmon_driver); + +MODULE_DESCRIPTION("NXP MC33XS2410 hwmon driver"); +MODULE_AUTHOR("Dimitri Fedrau "); +MODULE_LICENSE("GPL"); -- GitLab From 0f0a7bd04e7e00cef6da5f4955749d6f1fc27b32 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 10 Jun 2025 15:29:42 +0000 Subject: [PATCH 842/868] dt-bindings: gpio: Add Apple Mac SMC GPIO block Add the DT binding for the Apple Mac System Management Controller GPIOs. Signed-off-by: "Russell King (Oracle)" Reviewed-by: Linus Walleij Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Reviewed-by: "Rob Herring (Arm)" Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-1-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- .../bindings/gpio/apple,smc-gpio.yaml | 29 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml b/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml new file mode 100644 index 0000000000000..42b1bc0a10c97 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/apple,smc-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Mac System Management Controller GPIO + +maintainers: + - Sven Peter + +description: + Apple Mac System Management Controller GPIO block. + +properties: + compatible: + const: apple,smc-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + +required: + - compatible + - gpio-controller + - '#gpio-cells' + +additionalProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163..68097b1a44251 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2331,6 +2331,7 @@ F: Documentation/devicetree/bindings/arm/apple/* F: Documentation/devicetree/bindings/clock/apple,nco.yaml F: Documentation/devicetree/bindings/cpufreq/apple,cluster-cpufreq.yaml F: Documentation/devicetree/bindings/dma/apple,admac.yaml +F: Documentation/devicetree/bindings/gpio/apple,smc-gpio.yaml F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml F: Documentation/devicetree/bindings/input/touchscreen/apple,z2-multitouch.yaml F: Documentation/devicetree/bindings/interrupt-controller/apple,* -- GitLab From 51bb1f6d4694cd84491847ea59eb194311b7d7f8 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Tue, 10 Jun 2025 15:29:43 +0000 Subject: [PATCH 843/868] dt-bindings: power: reboot: Add Apple Mac SMC Reboot Controller On Apple Silicon machines a clean shutdown or reboot requires talking to SMC and writing to NVMEM cells. Add a binding for this MFD sub-device. Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Reviewed-by: "Rob Herring (Arm)" Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-2-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- .../power/reset/apple,smc-reboot.yaml | 40 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml diff --git a/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml b/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml new file mode 100644 index 0000000000000..ce5ed88493cdb --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/reset/apple,smc-reboot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SMC Reboot Controller + +description: + The Apple System Management Controller (SMC) provides reboot functionality + on Apple Silicon SoCs. It uses NVMEM cells to store and track various + system state information related to boot, shutdown, and panic events. + +maintainers: + - Sven Peter + +properties: + compatible: + const: apple,smc-reboot + + nvmem-cells: + items: + - description: Flag indicating shutdown (as opposed to reboot) + - description: Stage at which the boot process stopped (0x30 for normal boot) + - description: Counter for boot errors + - description: Counter for system panics + + nvmem-cell-names: + items: + - const: shutdown_flag + - const: boot_stage + - const: boot_error_count + - const: panic_count + +required: + - compatible + - nvmem-cells + - nvmem-cell-names + +additionalProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index 68097b1a44251..1dd004f7dff66 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2346,6 +2346,7 @@ F: Documentation/devicetree/bindings/nvmem/apple,spmi-nvmem.yaml F: Documentation/devicetree/bindings/pci/apple,pcie.yaml F: Documentation/devicetree/bindings/pinctrl/apple,pinctrl.yaml F: Documentation/devicetree/bindings/power/apple* +F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml -- GitLab From dbad719958e162ac021716c223ba9df9071bca55 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Tue, 10 Jun 2025 15:29:44 +0000 Subject: [PATCH 844/868] dt-bindings: mfd: Add Apple Mac System Management Controller Add a DT binding for the Apple Mac System Management Controller. Signed-off-by: "Russell King (Oracle)" Reviewed-by: Linus Walleij Reviewed-by: Krzysztof Kozlowski Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-3-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/apple,smc.yaml | 79 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/apple,smc.yaml diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml new file mode 100644 index 0000000000000..8a10e270d421e --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/apple,smc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Mac System Management Controller + +maintainers: + - Sven Peter + +description: + Apple Mac System Management Controller implements various functions + such as GPIO, RTC, power, reboot. + +properties: + compatible: + items: + - enum: + - apple,t6000-smc + - apple,t8103-smc + - apple,t8112-smc + - const: apple,smc + + reg: + items: + - description: SMC area + - description: SRAM area + + reg-names: + items: + - const: smc + - const: sram + + mboxes: + maxItems: 1 + + gpio: + $ref: /schemas/gpio/apple,smc-gpio.yaml + + reboot: + $ref: /schemas/power/reset/apple,smc-reboot.yaml + +additionalProperties: false + +required: + - compatible + - reg + - reg-names + - mboxes + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + smc@23e400000 { + compatible = "apple,t8103-smc", "apple,smc"; + reg = <0x2 0x3e400000 0x0 0x4000>, + <0x2 0x3fe00000 0x0 0x100000>; + reg-names = "smc", "sram"; + mboxes = <&smc_mbox>; + + smc_gpio: gpio { + compatible = "apple,smc-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + reboot { + compatible = "apple,smc-reboot"; + nvmem-cells = <&shutdown_flag>, <&boot_stage>, + <&boot_error_count>, <&panic_count>; + nvmem-cell-names = "shutdown_flag", "boot_stage", + "boot_error_count", "panic_count"; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 1dd004f7dff66..329b2df08d4b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2339,6 +2339,7 @@ F: Documentation/devicetree/bindings/iommu/apple,dart.yaml F: Documentation/devicetree/bindings/iommu/apple,sart.yaml F: Documentation/devicetree/bindings/leds/backlight/apple,dwi-bl.yaml F: Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml +F: Documentation/devicetree/bindings/mfd/apple,smc.yaml F: Documentation/devicetree/bindings/net/bluetooth/brcm,bcm4377-bluetooth.yaml F: Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml F: Documentation/devicetree/bindings/nvmem/apple,efuses.yaml -- GitLab From ba9ae011e8373b1ff34aa4175c79288013de7fc8 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Tue, 10 Jun 2025 15:29:45 +0000 Subject: [PATCH 845/868] soc: apple: rtkit: Make shmem_destroy optional shmem_destroy isn't always required for coprocessor-managed buffers but we still enforce that it exists. Just relax the check. Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-4-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- drivers/soc/apple/rtkit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index 5fffd0f003dc2..b8d4da147d23f 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -279,8 +279,7 @@ static int apple_rtkit_common_rx_get_buffer(struct apple_rtkit *rtk, dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n", buffer->size, &buffer->iova); - if (buffer->iova && - (!rtk->ops->shmem_setup || !rtk->ops->shmem_destroy)) { + if (buffer->iova && !rtk->ops->shmem_setup) { err = -EINVAL; goto error; } -- GitLab From e038d985c9823a12cd64fa077d0c5aca2c644b67 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Tue, 10 Jun 2025 15:29:46 +0000 Subject: [PATCH 846/868] mfd: Add Apple Silicon System Management Controller The System Management Controller (SMC) on Apple Silicon machines is a piece of hardware that exposes various functionalities such as temperature sensors, voltage/power meters, shutdown/reboot handling, GPIOs and more. Communication happens via a shared mailbox using the RTKit protocol which is also used for other co-processors. The SMC protocol then allows reading and writing many different keys which implement the various features. The MFD core device handles this protocol and exposes it to the sub-devices. Some of the sub-devices are potentially also useful on pre-M1 Apple machines and support for SMCs on these machines can be added at a later time. Co-developed-by: Hector Martin Signed-off-by: Hector Martin Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-5-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- MAINTAINERS | 2 + drivers/mfd/Kconfig | 18 ++ drivers/mfd/Makefile | 1 + drivers/mfd/macsmc.c | 498 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/macsmc.h | 279 +++++++++++++++++++++ 5 files changed, 798 insertions(+) create mode 100644 drivers/mfd/macsmc.c create mode 100644 include/linux/mfd/macsmc.h diff --git a/MAINTAINERS b/MAINTAINERS index 329b2df08d4b1..f6bf4643c20b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2364,6 +2364,7 @@ F: drivers/input/touchscreen/apple_z2.c F: drivers/iommu/apple-dart.c F: drivers/iommu/io-pgtable-dart.c F: drivers/irqchip/irq-apple-aic.c +F: drivers/mfd/macsmc.c F: drivers/nvme/host/apple.c F: drivers/nvmem/apple-efuses.c F: drivers/nvmem/apple-spmi-nvmem.c @@ -2376,6 +2377,7 @@ F: drivers/video/backlight/apple_dwi_bl.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/pinctrl/apple.h +F: include/linux/mfd/macsmc.h F: include/linux/soc/apple/* F: include/uapi/drm/asahi_drm.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6fb3768e3d71c..c6cc423608877 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -285,6 +285,24 @@ config MFD_CS42L43_SDW Select this to support the Cirrus Logic CS42L43 PC CODEC with headphone and class D speaker drivers over SoundWire. +config MFD_MACSMC + tristate "Apple Silicon System Management Controller (SMC)" + depends on ARCH_APPLE || COMPILE_TEST + depends on OF + depends on APPLE_RTKIT + select MFD_CORE + help + The System Management Controller (SMC) on Apple Silicon machines is a + piece of hardware that exposes various functionalities such as + temperature sensors, voltage/power meters, shutdown/reboot handling, + GPIOs and more. + + Communication happens via a shared mailbox using the RTKit protocol + which is also used for other co-processors. The SMC protocol then + allows reading and writing many different keys which implement the + various features. The MFD core device handles this protocol and + exposes it to the sub-devices. + config MFD_MADERA tristate "Cirrus Logic Madera codecs" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 79495f9f3457b..f7bdedd5a66d1 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o +obj-$(CONFIG_MFD_MACSMC) += macsmc.o obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c new file mode 100644 index 0000000000000..870c8b2028a8f --- /dev/null +++ b/drivers/mfd/macsmc.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC (System Management Controller) MFD driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMC_ENDPOINT 0x20 + +/* We don't actually know the true size here but this seem reasonable */ +#define SMC_SHMEM_SIZE 0x1000 +#define SMC_MAX_SIZE 255 + +#define SMC_MSG_READ_KEY 0x10 +#define SMC_MSG_WRITE_KEY 0x11 +#define SMC_MSG_GET_KEY_BY_INDEX 0x12 +#define SMC_MSG_GET_KEY_INFO 0x13 +#define SMC_MSG_INITIALIZE 0x17 +#define SMC_MSG_NOTIFICATION 0x18 +#define SMC_MSG_RW_KEY 0x20 + +#define SMC_DATA GENMASK_ULL(63, 32) +#define SMC_WSIZE GENMASK_ULL(31, 24) +#define SMC_SIZE GENMASK_ULL(23, 16) +#define SMC_ID GENMASK_ULL(15, 12) +#define SMC_MSG GENMASK_ULL(7, 0) +#define SMC_RESULT SMC_MSG + +#define SMC_TIMEOUT_MS 500 + +static const struct mfd_cell apple_smc_devs[] = { + MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-gpio"), + MFD_CELL_OF("macsmc-reboot", NULL, NULL, 0, 0, "apple,smc-reboot"), +}; + +static int apple_smc_cmd_locked(struct apple_smc *smc, u64 cmd, u64 arg, + u64 size, u64 wsize, u32 *ret_data) +{ + u8 result; + int ret; + u64 msg; + + lockdep_assert_held(&smc->mutex); + + if (smc->boot_stage != APPLE_SMC_INITIALIZED) + return -EIO; + if (smc->atomic_mode) + return -EIO; + + reinit_completion(&smc->cmd_done); + + smc->msg_id = (smc->msg_id + 1) & 0xf; + msg = (FIELD_PREP(SMC_MSG, cmd) | + FIELD_PREP(SMC_SIZE, size) | + FIELD_PREP(SMC_WSIZE, wsize) | + FIELD_PREP(SMC_ID, smc->msg_id) | + FIELD_PREP(SMC_DATA, arg)); + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, false); + if (ret) { + dev_err(smc->dev, "Failed to send command\n"); + return ret; + } + + if (wait_for_completion_timeout(&smc->cmd_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) <= 0) { + dev_err(smc->dev, "Command timed out (%llx)", msg); + return -ETIMEDOUT; + } + + if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) { + dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n", + smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret)); + return -EIO; + } + + result = FIELD_GET(SMC_RESULT, smc->cmd_ret); + if (result) + return -EIO; + + if (ret_data) + *ret_data = FIELD_GET(SMC_DATA, smc->cmd_ret); + + return FIELD_GET(SMC_SIZE, smc->cmd_ret); +} + +static int apple_smc_cmd(struct apple_smc *smc, u64 cmd, u64 arg, + u64 size, u64 wsize, u32 *ret_data) +{ + guard(mutex)(&smc->mutex); + + return apple_smc_cmd_locked(smc, cmd, arg, size, wsize, ret_data); +} + +static int apple_smc_rw_locked(struct apple_smc *smc, smc_key key, + const void *wbuf, size_t wsize, + void *rbuf, size_t rsize) +{ + u64 smc_size, smc_wsize; + u32 rdata; + int ret; + u64 cmd; + + lockdep_assert_held(&smc->mutex); + + if (rsize > SMC_MAX_SIZE) + return -EINVAL; + if (wsize > SMC_MAX_SIZE) + return -EINVAL; + + if (rsize && wsize) { + cmd = SMC_MSG_RW_KEY; + memcpy_toio(smc->shmem.iomem, wbuf, wsize); + smc_size = rsize; + smc_wsize = wsize; + } else if (wsize && !rsize) { + cmd = SMC_MSG_WRITE_KEY; + memcpy_toio(smc->shmem.iomem, wbuf, wsize); + /* + * Setting size to the length we want to write and wsize to 0 + * looks silly but that's how the SMC protocol works ¯\_(ツ)_/¯ + */ + smc_size = wsize; + smc_wsize = 0; + } else if (!wsize && rsize) { + cmd = SMC_MSG_READ_KEY; + smc_size = rsize; + smc_wsize = 0; + } else { + return -EINVAL; + } + + ret = apple_smc_cmd_locked(smc, cmd, key, smc_size, smc_wsize, &rdata); + if (ret < 0) + return ret; + + if (rsize) { + /* + * Small data <= 4 bytes is returned as part of the reply + * message which is sent over the mailbox FIFO. Everything + * bigger has to be copied from SRAM which is mapped as + * Device memory. + */ + if (rsize <= 4) + memcpy(rbuf, &rdata, rsize); + else + memcpy_fromio(rbuf, smc->shmem.iomem, rsize); + } + + return ret; +} + +int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + guard(mutex)(&smc->mutex); + + return apple_smc_rw_locked(smc, key, NULL, 0, buf, size); +} +EXPORT_SYMBOL(apple_smc_read); + +int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + guard(mutex)(&smc->mutex); + + return apple_smc_rw_locked(smc, key, buf, size, NULL, 0); +} +EXPORT_SYMBOL(apple_smc_write); + +int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, + void *rbuf, size_t rsize) +{ + guard(mutex)(&smc->mutex); + + return apple_smc_rw_locked(smc, key, wbuf, wsize, rbuf, rsize); +} +EXPORT_SYMBOL(apple_smc_rw); + +int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key) +{ + int ret; + + ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_BY_INDEX, index, 0, 0, key); + + *key = swab32(*key); + return ret; +} +EXPORT_SYMBOL(apple_smc_get_key_by_index); + +int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info) +{ + u8 key_info[6]; + int ret; + + ret = apple_smc_cmd(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL); + if (ret >= 0 && info) { + memcpy_fromio(key_info, smc->shmem.iomem, sizeof(key_info)); + info->size = key_info[0]; + info->type_code = get_unaligned_be32(&key_info[1]); + info->flags = key_info[5]; + } + return ret; +} +EXPORT_SYMBOL(apple_smc_get_key_info); + +int apple_smc_enter_atomic(struct apple_smc *smc) +{ + guard(mutex)(&smc->mutex); + + /* + * Disable notifications since this is called before shutdown and no + * notification handler will be able to handle the notification + * using atomic operations only. Also ignore any failure here + * because we're about to shut down or reboot anyway. + * We can't use apple_smc_write_flag here since that would try to lock + * smc->mutex again. + */ + const u8 flag = 0; + + apple_smc_rw_locked(smc, SMC_KEY(NTAP), &flag, sizeof(flag), NULL, 0); + + smc->atomic_mode = true; + + return 0; +} +EXPORT_SYMBOL(apple_smc_enter_atomic); + +int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size) +{ + guard(spinlock_irqsave)(&smc->lock); + u8 result; + int ret; + u64 msg; + + if (size > SMC_MAX_SIZE || size == 0) + return -EINVAL; + + if (smc->boot_stage != APPLE_SMC_INITIALIZED) + return -EIO; + if (!smc->atomic_mode) + return -EIO; + + memcpy_toio(smc->shmem.iomem, buf, size); + smc->msg_id = (smc->msg_id + 1) & 0xf; + msg = (FIELD_PREP(SMC_MSG, SMC_MSG_WRITE_KEY) | + FIELD_PREP(SMC_SIZE, size) | + FIELD_PREP(SMC_ID, smc->msg_id) | + FIELD_PREP(SMC_DATA, key)); + smc->atomic_pending = true; + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, msg, NULL, true); + if (ret < 0) { + dev_err(smc->dev, "Failed to send command (%d)\n", ret); + return ret; + } + + while (smc->atomic_pending) { + ret = apple_rtkit_poll(smc->rtk); + if (ret < 0) { + dev_err(smc->dev, "RTKit poll failed (%llx)", msg); + return ret; + } + udelay(100); + } + + if (FIELD_GET(SMC_ID, smc->cmd_ret) != smc->msg_id) { + dev_err(smc->dev, "Command sequence mismatch (expected %d, got %d)\n", + smc->msg_id, (unsigned int)FIELD_GET(SMC_ID, smc->cmd_ret)); + return -EIO; + } + + result = FIELD_GET(SMC_RESULT, smc->cmd_ret); + if (result) + return -EIO; + + return FIELD_GET(SMC_SIZE, smc->cmd_ret); +} +EXPORT_SYMBOL(apple_smc_write_atomic); + +static void apple_smc_rtkit_crashed(void *cookie, const void *bfr, size_t bfr_len) +{ + struct apple_smc *smc = cookie; + + smc->boot_stage = APPLE_SMC_ERROR_CRASHED; + dev_err(smc->dev, "SMC crashed! Your system will reboot in a few seconds...\n"); +} + +static int apple_smc_rtkit_shmem_setup(void *cookie, struct apple_rtkit_shmem *bfr) +{ + struct apple_smc *smc = cookie; + size_t bfr_end; + + if (!bfr->iova) { + dev_err(smc->dev, "RTKit wants a RAM buffer\n"); + return -EIO; + } + + if (check_add_overflow(bfr->iova, bfr->size - 1, &bfr_end)) + return -EFAULT; + + if (bfr->iova < smc->sram->start || bfr->iova > smc->sram->end || + bfr_end > smc->sram->end) { + dev_err(smc->dev, "RTKit buffer request outside SRAM region: [0x%llx, 0x%llx]\n", + (unsigned long long)bfr->iova, + (unsigned long long)bfr_end); + return -EFAULT; + } + + bfr->iomem = smc->sram_base + (bfr->iova - smc->sram->start); + bfr->is_mapped = true; + + return 0; +} + +static bool apple_smc_rtkit_recv_early(void *cookie, u8 endpoint, u64 message) +{ + struct apple_smc *smc = cookie; + + if (endpoint != SMC_ENDPOINT) { + dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint); + return false; + } + + if (smc->boot_stage == APPLE_SMC_BOOTING) { + int ret; + + smc->shmem.iova = message; + smc->shmem.size = SMC_SHMEM_SIZE; + ret = apple_smc_rtkit_shmem_setup(smc, &smc->shmem); + if (ret < 0) { + smc->boot_stage = APPLE_SMC_ERROR_NO_SHMEM; + dev_err(smc->dev, "Failed to initialize shared memory (%d)\n", ret); + } else { + smc->boot_stage = APPLE_SMC_INITIALIZED; + } + complete(&smc->init_done); + } else if (FIELD_GET(SMC_MSG, message) == SMC_MSG_NOTIFICATION) { + /* Handle these in the RTKit worker thread */ + return false; + } else { + smc->cmd_ret = message; + if (smc->atomic_pending) + smc->atomic_pending = false; + else + complete(&smc->cmd_done); + } + + return true; +} + +static void apple_smc_rtkit_recv(void *cookie, u8 endpoint, u64 message) +{ + struct apple_smc *smc = cookie; + + if (endpoint != SMC_ENDPOINT) { + dev_warn(smc->dev, "Received message for unknown endpoint 0x%x\n", endpoint); + return; + } + + if (FIELD_GET(SMC_MSG, message) != SMC_MSG_NOTIFICATION) { + dev_warn(smc->dev, "Received unknown message from worker: 0x%llx\n", message); + return; + } + + blocking_notifier_call_chain(&smc->event_handlers, FIELD_GET(SMC_DATA, message), NULL); +} + +static const struct apple_rtkit_ops apple_smc_rtkit_ops = { + .crashed = apple_smc_rtkit_crashed, + .recv_message = apple_smc_rtkit_recv, + .recv_message_early = apple_smc_rtkit_recv_early, + .shmem_setup = apple_smc_rtkit_shmem_setup, +}; + +static void apple_smc_rtkit_shutdown(void *data) +{ + struct apple_smc *smc = data; + + /* Shut down SMC firmware, if it's not completely wedged */ + if (apple_rtkit_is_running(smc->rtk)) + apple_rtkit_quiesce(smc->rtk); +} + +static void apple_smc_disable_notifications(void *data) +{ + struct apple_smc *smc = data; + + apple_smc_write_flag(smc, SMC_KEY(NTAP), false); +} + +static int apple_smc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_smc *smc; + u32 count; + int ret; + + smc = devm_kzalloc(dev, sizeof(*smc), GFP_KERNEL); + if (!smc) + return -ENOMEM; + + smc->dev = &pdev->dev; + smc->sram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &smc->sram); + if (IS_ERR(smc->sram_base)) + return dev_err_probe(dev, PTR_ERR(smc->sram_base), "Failed to map SRAM region"); + + smc->rtk = devm_apple_rtkit_init(dev, smc, NULL, 0, &apple_smc_rtkit_ops); + if (IS_ERR(smc->rtk)) + return dev_err_probe(dev, PTR_ERR(smc->rtk), "Failed to initialize RTKit"); + + smc->boot_stage = APPLE_SMC_BOOTING; + ret = apple_rtkit_wake(smc->rtk); + if (ret) + return dev_err_probe(dev, ret, "Failed to wake up SMC"); + + ret = devm_add_action_or_reset(dev, apple_smc_rtkit_shutdown, smc); + if (ret) + return dev_err_probe(dev, ret, "Failed to register rtkit shutdown action"); + + ret = apple_rtkit_start_ep(smc->rtk, SMC_ENDPOINT); + if (ret) + return dev_err_probe(dev, ret, "Failed to start SMC endpoint"); + + init_completion(&smc->init_done); + init_completion(&smc->cmd_done); + + ret = apple_rtkit_send_message(smc->rtk, SMC_ENDPOINT, + FIELD_PREP(SMC_MSG, SMC_MSG_INITIALIZE), NULL, false); + if (ret) + return dev_err_probe(dev, ret, "Failed to send init message"); + + if (wait_for_completion_timeout(&smc->init_done, msecs_to_jiffies(SMC_TIMEOUT_MS)) == 0) { + dev_err(dev, "Timed out initializing SMC"); + return -ETIMEDOUT; + } + + if (smc->boot_stage != APPLE_SMC_INITIALIZED) { + dev_err(dev, "SMC failed to boot successfully, boot stage=%d\n", smc->boot_stage); + return -EIO; + } + + dev_set_drvdata(&pdev->dev, smc); + BLOCKING_INIT_NOTIFIER_HEAD(&smc->event_handlers); + + ret = apple_smc_read_u32(smc, SMC_KEY(#KEY), &count); + if (ret) + return dev_err_probe(smc->dev, ret, "Failed to get key count"); + smc->key_count = be32_to_cpu(count); + + /* Enable notifications */ + apple_smc_write_flag(smc, SMC_KEY(NTAP), true); + ret = devm_add_action_or_reset(dev, apple_smc_disable_notifications, smc); + if (ret) + return dev_err_probe(dev, ret, "Failed to register notification disable action"); + + ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE, + apple_smc_devs, ARRAY_SIZE(apple_smc_devs), + NULL, 0, NULL); + if (ret) + return dev_err_probe(smc->dev, ret, "Failed to register sub-devices"); + + + return 0; +} + +static const struct of_device_id apple_smc_of_match[] = { + { .compatible = "apple,smc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_smc_of_match); + +static struct platform_driver apple_smc_driver = { + .driver = { + .name = "macsmc", + .of_match_table = apple_smc_of_match, + }, + .probe = apple_smc_probe, +}; +module_platform_driver(apple_smc_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_AUTHOR("Sven Peter "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC driver"); diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h new file mode 100644 index 0000000000000..6b13f01a85924 --- /dev/null +++ b/include/linux/mfd/macsmc.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple SMC (System Management Controller) core definitions + * + * Copyright (C) The Asahi Linux Contributors + */ + +#ifndef _LINUX_MFD_MACSMC_H +#define _LINUX_MFD_MACSMC_H + +#include + +/** + * typedef smc_key - Alias for u32 to be used for SMC keys + * + * SMC keys are 32bit integers containing packed ASCII characters in natural + * integer order, i.e. 0xAABBCCDD, which represent the FourCC ABCD. + * The SMC driver is designed with this assumption and ensures the right + * endianness is used when these are stored to memory and sent to or received + * from the actual SMC firmware (which can be done in either shared memory or + * as 64bit mailbox message on Apple Silicon). + * Internally, SMC stores these keys in a table sorted lexicographically and + * allows resolving an index into this table to the corresponding SMC key. + * Thus, storing keys as u32 is very convenient as it allows to e.g. use + * normal comparison operators which directly map to the natural order used + * by SMC firmware. + * + * This simple type alias is introduced to allow easy recognition of SMC key + * variables and arguments. + */ +typedef u32 smc_key; + +/** + * SMC_KEY - Convert FourCC SMC keys in source code to smc_key + * + * This macro can be used to easily define FourCC SMC keys in source code + * and convert these to u32 / smc_key, e.g. SMC_KEY(NTAP) will expand to + * 0x4e544150. + * + * @s: FourCC SMC key to be converted + */ +#define SMC_KEY(s) (smc_key)(_SMC_KEY(#s)) +#define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3]) + +#define APPLE_SMC_READABLE BIT(7) +#define APPLE_SMC_WRITABLE BIT(6) +#define APPLE_SMC_FUNCTION BIT(4) + +/** + * struct apple_smc_key_info - Information for a SMC key as returned by SMC + * @type_code: FourCC code indicating the type for this key. + * Known types: + * ch8*: ASCII string + * flag: Boolean, 1 or 0 + * flt: 32-bit single-precision IEEE 754 float + * hex: Binary data + * ioft: 64bit Unsigned fixed-point intger (48.16) + * {si,ui}{8,16,32,64}: Signed/Unsigned 8-/16-/32-/64-bit integer + * @size: Size of the buffer associated with this key + * @flags: Bitfield encoding flags (APPLE_SMC_{READABLE,WRITABLE,FUNCTION}) + */ +struct apple_smc_key_info { + u32 type_code; + u8 size; + u8 flags; +}; + +/** + * enum apple_smc_boot_stage - SMC boot stage + * @APPLE_SMC_BOOTING: SMC is booting + * @APPLE_SMC_INITIALIZED: SMC is initialized and ready to use + * @APPLE_SMC_ERROR_NO_SHMEM: Shared memory could not be initialized during boot + * @APPLE_SMC_ERROR_CRASHED: SMC has crashed + */ +enum apple_smc_boot_stage { + APPLE_SMC_BOOTING, + APPLE_SMC_INITIALIZED, + APPLE_SMC_ERROR_NO_SHMEM, + APPLE_SMC_ERROR_CRASHED +}; + +/** + * struct apple_smc + * @dev: Underlying device struct for the physical backend device + * @key_count: Number of available SMC keys + * @first_key: First valid SMC key + * @last_key: Last valid SMC key + * @event_handlers: Notifier call chain for events received from SMC + * @rtk: Pointer to Apple RTKit instance + * @init_done: Completion for initialization + * @boot_stage: Current boot stage of SMC + * @sram: Pointer to SRAM resource + * @sram_base: SRAM base address + * @shmem: RTKit shared memory structure for SRAM + * @msg_id: Current message id for commands, will be incremented for each command + * @atomic_mode: Flag set when atomic mode is entered + * @atomic_pending: Flag indicating pending atomic command + * @cmd_done: Completion for command execution in non-atomic mode + * @cmd_ret: Return value from SMC for last command + * @mutex: Mutex for non-atomic mode + * @lock: Spinlock for atomic mode + */ +struct apple_smc { + struct device *dev; + + u32 key_count; + smc_key first_key; + smc_key last_key; + + struct blocking_notifier_head event_handlers; + + struct apple_rtkit *rtk; + + struct completion init_done; + enum apple_smc_boot_stage boot_stage; + + struct resource *sram; + void __iomem *sram_base; + struct apple_rtkit_shmem shmem; + + unsigned int msg_id; + + bool atomic_mode; + bool atomic_pending; + struct completion cmd_done; + u64 cmd_ret; + + struct mutex mutex; + spinlock_t lock; +}; + +/** + * apple_smc_read - Read size bytes from given SMC key into buf + * @smc: Pointer to apple_smc struct + * @key: smc_key to be read + * @buf: Buffer into which size bytes of data will be read from SMC + * @size: Number of bytes to be read into buf + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size); + +/** + * apple_smc_write - Write size bytes into given SMC key from buf + * @smc: Pointer to apple_smc struct + * @key: smc_key data will be written to + * @buf: Buffer from which size bytes of data will be written to SMC + * @size: Number of bytes to be written + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size); + +/** + * apple_smc_enter_atomic - Enter atomic mode to be able to use apple_smc_write_atomic + * @smc: Pointer to apple_smc struct + * + * This function switches the SMC backend to atomic mode which allows the + * use of apple_smc_write_atomic while disabling *all* other functions. + * This is only used for shutdown/reboot which requires writing to a SMC + * key from atomic context. + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_enter_atomic(struct apple_smc *smc); + +/** + * apple_smc_write_atomic - Write size bytes into given SMC key from buf without sleeping + * @smc: Pointer to apple_smc struct + * @key: smc_key data will be written to + * @buf: Buffer from which size bytes of data will be written to SMC + * @size: Number of bytes to be written + * + * Note that this function will fail if apple_smc_enter_atomic hasn't been + * called before. + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size); + +/** + * apple_smc_rw - Write and then read using the given SMC key + * @smc: Pointer to apple_smc struct + * @key: smc_key data will be written to + * @wbuf: Buffer from which size bytes of data will be written to SMC + * @wsize: Number of bytes to be written + * @rbuf: Buffer to which size bytes of data will be read from SMC + * @rsize: Number of bytes to be read + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize, + void *rbuf, size_t rsize); + +/** + * apple_smc_get_key_by_index - Given an index return the corresponding SMC key + * @smc: Pointer to apple_smc struct + * @index: Index to be resolved + * @key: Buffer for SMC key to be returned + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key); + +/** + * apple_smc_get_key_info - Get key information from SMC + * @smc: Pointer to apple_smc struct + * @key: Key to acquire information for + * @info: Pointer to struct apple_smc_key_info which will be filled + * + * Return: Zero on success, negative errno on error + */ +int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info); + +/** + * apple_smc_key_exists - Check if the given SMC key exists + * @smc: Pointer to apple_smc struct + * @key: smc_key to be checked + * + * Return: True if the key exists, false otherwise + */ +static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key) +{ + return apple_smc_get_key_info(smc, key, NULL) >= 0; +} + +#define APPLE_SMC_TYPE_OPS(type) \ + static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \ + { \ + int ret = apple_smc_read(smc, key, p, sizeof(*p)); \ + return (ret < 0) ? ret : ((ret != sizeof(*p)) ? -EINVAL : 0); \ + } \ + static inline int apple_smc_write_##type(struct apple_smc *smc, smc_key key, type p) \ + { \ + return apple_smc_write(smc, key, &p, sizeof(p)); \ + } \ + static inline int apple_smc_write_##type##_atomic(struct apple_smc *smc, smc_key key, type p) \ + { \ + return apple_smc_write_atomic(smc, key, &p, sizeof(p)); \ + } \ + static inline int apple_smc_rw_##type(struct apple_smc *smc, smc_key key, \ + type w, type *r) \ + { \ + int ret = apple_smc_rw(smc, key, &w, sizeof(w), r, sizeof(*r)); \ + return (ret < 0) ? ret : ((ret != sizeof(*r)) ? -EINVAL : 0); \ + } + +APPLE_SMC_TYPE_OPS(u64) +APPLE_SMC_TYPE_OPS(u32) +APPLE_SMC_TYPE_OPS(u16) +APPLE_SMC_TYPE_OPS(u8) +APPLE_SMC_TYPE_OPS(s64) +APPLE_SMC_TYPE_OPS(s32) +APPLE_SMC_TYPE_OPS(s16) +APPLE_SMC_TYPE_OPS(s8) + +static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key, bool *flag) +{ + u8 val; + int ret = apple_smc_read_u8(smc, key, &val); + + if (ret < 0) + return ret; + + *flag = val ? true : false; + return ret; +} + +static inline int apple_smc_write_flag(struct apple_smc *smc, smc_key key, bool state) +{ + return apple_smc_write_u8(smc, key, state ? 1 : 0); +} + +static inline int apple_smc_write_flag_atomic(struct apple_smc *smc, smc_key key, bool state) +{ + return apple_smc_write_u8_atomic(smc, key, state ? 1 : 0); +} + +#endif -- GitLab From 9b21051b0885912f5bb2cc9d4f95c6fca697da4d Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 10 Jun 2025 15:29:47 +0000 Subject: [PATCH 847/868] gpio: Add new gpio-macsmc driver for Apple Macs This driver implements the GPIO service on top of the SMC framework on Apple Mac machines. In particular, these are the GPIOs present in the PMU IC which are used to control power to certain on-board devices. Although the underlying hardware supports various pin config settings (input/output, open drain, etc.), this driver does not implement that functionality and leaves it up to the firmware to configure things properly. We also don't yet support interrupts/events. This is sufficient for device power control, which is the only thing we need to support at this point. More features will be implemented when needed. To our knowledge, only Apple Silicon Macs implement this SMC feature. Signed-off-by: Hector Martin Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Reviewed-by: Sven Peter Signed-off-by: "Russell King (Oracle)" Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Signed-off-by: Sven Peter Acked-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-6-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 10 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-macsmc.c | 292 +++++++++++++++++++++++++++++++++++++ 4 files changed, 304 insertions(+) create mode 100644 drivers/gpio/gpio-macsmc.c diff --git a/MAINTAINERS b/MAINTAINERS index f6bf4643c20b6..4c1977d0af7c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2357,6 +2357,7 @@ F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/cpufreq/apple-soc-cpufreq.c F: drivers/dma/apple-admac.c +F: drivers/gpio/gpio-macsmc.c F: drivers/pmdomain/apple/ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 44f922e10db2f..8a58df00faa1f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1464,6 +1464,16 @@ config GPIO_LP87565 This driver can also be built as a module. If so, the module will be called gpio-lp87565. +config GPIO_MACSMC + tristate "Apple Mac SMC GPIO" + depends on MFD_MACSMC + help + Support for GPIOs controlled by the SMC microcontroller on Apple Mac + systems. + + This driver can also be built as a module. If so, the module will be + called gpio-macsmc. + config GPIO_MADERA tristate "Cirrus Logic Madera class codecs" depends on PINCTRL_MADERA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 88dedd298256c..3e5bc90ba59e5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c new file mode 100644 index 0000000000000..7570d9e89adff --- /dev/null +++ b/drivers/gpio/gpio-macsmc.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC GPIO driver + * Copyright The Asahi Linux Contributors + * + * This driver implements basic SMC PMU GPIO support that can read inputs + * and write outputs. Mode changes and IRQ config are not yet implemented. + */ + +#include +#include +#include +#include +#include + +#define MAX_GPIO 64 + +/* + * Commands 0-6 are, presumably, the intended API. + * Command 0xff lets you get/set the pin configuration in detail directly, + * but the bit meanings seem not to be stable between devices/PMU hardware + * versions. + * + * We're going to try to make do with the low commands for now. + * We don't implement pin mode changes at this time. + */ + +#define CMD_ACTION (0 << 24) +#define CMD_OUTPUT (1 << 24) +#define CMD_INPUT (2 << 24) +#define CMD_PINMODE (3 << 24) +#define CMD_IRQ_ENABLE (4 << 24) +#define CMD_IRQ_ACK (5 << 24) +#define CMD_IRQ_MODE (6 << 24) +#define CMD_CONFIG (0xff << 24) + +#define MODE_INPUT 0 +#define MODE_OUTPUT 1 +#define MODE_VALUE_0 0 +#define MODE_VALUE_1 2 + +#define IRQ_MODE_HIGH 0 +#define IRQ_MODE_LOW 1 +#define IRQ_MODE_RISING 2 +#define IRQ_MODE_FALLING 3 +#define IRQ_MODE_BOTH 4 + +#define CONFIG_MASK GENMASK(23, 16) +#define CONFIG_VAL GENMASK(7, 0) + +#define CONFIG_OUTMODE GENMASK(7, 6) +#define CONFIG_IRQMODE GENMASK(5, 3) +#define CONFIG_PULLDOWN BIT(2) +#define CONFIG_PULLUP BIT(1) +#define CONFIG_OUTVAL BIT(0) + +/* + * Output modes seem to differ depending on the PMU in use... ? + * j274 / M1 (Sera PMU): + * 0 = input + * 1 = output + * 2 = open drain + * 3 = disable + * j314 / M1Pro (Maverick PMU): + * 0 = input + * 1 = open drain + * 2 = output + * 3 = ? + */ + +struct macsmc_gpio { + struct device *dev; + struct apple_smc *smc; + struct gpio_chip gc; + + int first_index; +}; + +static int macsmc_gpio_nr(smc_key key) +{ + int low = hex_to_bin(key & 0xff); + int high = hex_to_bin((key >> 8) & 0xff); + + if (low < 0 || high < 0) + return -1; + + return low | (high << 4); +} + +static int macsmc_gpio_key(unsigned int offset) +{ + return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset); +} + +static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp) +{ + struct apple_smc *smc = smcgp->smc; + smc_key key = macsmc_gpio_key(0); + smc_key first_key, last_key; + int start, count, ret; + + /* Return early if the key is out of bounds */ + ret = apple_smc_get_key_by_index(smc, 0, &first_key); + if (ret) + return ret; + if (key <= first_key) + return -ENODEV; + + ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key); + if (ret) + return ret; + if (key > last_key) + return -ENODEV; + + /* Binary search to find index of first SMC key bigger or equal to key */ + start = 0; + count = smc->key_count; + while (count > 1) { + smc_key pkey; + int pivot = start + ((count - 1) >> 1); + + ret = apple_smc_get_key_by_index(smc, pivot, &pkey); + if (ret < 0) + return ret; + + if (pkey == key) + return pivot; + + pivot++; + + if (pkey < key) { + count -= pivot - start; + start = pivot; + } else { + count = pivot - start; + } + } + + return start; +} + +static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 val; + int ret; + + /* First try reading the explicit pin mode register */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val); + if (!ret) + return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; + + /* + * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode. + * Fall back to reading IRQ mode, which will only succeed for inputs. + */ + ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val); + return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + u32 cmd, val; + int ret; + + ret = macsmc_gpio_get_direction(gc, offset); + if (ret < 0) + return ret; + + if (ret == GPIO_LINE_DIRECTION_OUT) + cmd = CMD_OUTPUT; + else + cmd = CMD_INPUT; + + ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val); + if (ret < 0) + return ret; + + return val ? 1 : 0; +} + +static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + smc_key key = macsmc_gpio_key(offset); + int ret; + + value |= CMD_OUTPUT; + ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value); + if (ret < 0) + dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n", + &key, value); + + return ret; +} + +static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, unsigned int ngpios) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + int count; + int i; + + count = min(smcgp->smc->key_count, MAX_GPIO); + + bitmap_zero(valid_mask, ngpios); + + for (i = 0; i < count; i++) { + int ret, gpio_nr; + smc_key key; + + ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key); + if (ret < 0) + return ret; + + if (key > SMC_KEY(gPff)) + break; + + gpio_nr = macsmc_gpio_nr(key); + if (gpio_nr < 0 || gpio_nr > MAX_GPIO) { + dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key); + continue; + } + + set_bit(gpio_nr, valid_mask); + } + + return 0; +} + +static int macsmc_gpio_probe(struct platform_device *pdev) +{ + struct macsmc_gpio *smcgp; + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + smc_key key; + int ret; + + smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL); + if (!smcgp) + return -ENOMEM; + + smcgp->dev = &pdev->dev; + smcgp->smc = smc; + + smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp); + if (smcgp->first_index < 0) + return smcgp->first_index; + + ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key); + if (ret < 0) + return ret; + + if (key > macsmc_gpio_key(MAX_GPIO - 1)) + return -ENODEV; + + dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key); + + smcgp->gc.label = "macsmc-pmu-gpio"; + smcgp->gc.owner = THIS_MODULE; + smcgp->gc.get = macsmc_gpio_get; + smcgp->gc.set_rv = macsmc_gpio_set; + smcgp->gc.get_direction = macsmc_gpio_get_direction; + smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask; + smcgp->gc.can_sleep = true; + smcgp->gc.ngpio = MAX_GPIO; + smcgp->gc.base = -1; + smcgp->gc.parent = &pdev->dev; + + return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp); +} + +static const struct of_device_id macsmc_gpio_of_table[] = { + { .compatible = "apple,smc-gpio", }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table); + +static struct platform_driver macsmc_gpio_driver = { + .driver = { + .name = "macsmc-gpio", + .of_match_table = macsmc_gpio_of_table, + }, + .probe = macsmc_gpio_probe, +}; +module_platform_driver(macsmc_gpio_driver); + +MODULE_AUTHOR("Hector Martin "); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC GPIO driver"); -- GitLab From 819687eb28e501d21dabd6a3f52454638a815071 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Tue, 10 Jun 2025 15:29:48 +0000 Subject: [PATCH 848/868] power: reset: macsmc-reboot: Add driver for rebooting via Apple SMC This driver implements the reboot/shutdown support exposed by the SMC on Apple Silicon machines, such as Apple M1 Macs. Signed-off-by: Hector Martin Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Reviewed-by: Sebastian Reichel Signed-off-by: Sven Peter Link: https://lore.kernel.org/r/20250610-smc-6-15-v7-7-556cafd771d3@kernel.org Signed-off-by: Lee Jones --- MAINTAINERS | 1 + drivers/power/reset/Kconfig | 9 + drivers/power/reset/Makefile | 1 + drivers/power/reset/macsmc-reboot.c | 290 ++++++++++++++++++++++++++++ 4 files changed, 301 insertions(+) create mode 100644 drivers/power/reset/macsmc-reboot.c diff --git a/MAINTAINERS b/MAINTAINERS index 4c1977d0af7c9..9a3f273c557fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2370,6 +2370,7 @@ F: drivers/nvme/host/apple.c F: drivers/nvmem/apple-efuses.c F: drivers/nvmem/apple-spmi-nvmem.c F: drivers/pinctrl/pinctrl-apple-gpio.c +F: drivers/power/reset/macsmc-reboot.c F: drivers/pwm/pwm-apple.c F: drivers/soc/apple/* F: drivers/spi/spi-apple.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index e71f0af4e378c..733d812621599 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -128,6 +128,15 @@ config POWER_RESET_LINKSTATION Say Y here if you have a Buffalo LinkStation LS421D/E. +config POWER_RESET_MACSMC + tristate "Apple SMC reset/power-off driver" + depends on MFD_MACSMC + help + This driver supports reset and power-off on Apple Mac machines + that implement this functionality via the SMC. + + Say Y here if you have an Apple Silicon Mac. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1b9b63a1a8731..b7c2b5940be99 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o +obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o diff --git a/drivers/power/reset/macsmc-reboot.c b/drivers/power/reset/macsmc-reboot.c new file mode 100644 index 0000000000000..e9702acdd366b --- /dev/null +++ b/drivers/power/reset/macsmc-reboot.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC Reboot/Poweroff Handler + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct macsmc_reboot_nvmem { + struct nvmem_cell *shutdown_flag; + struct nvmem_cell *boot_stage; + struct nvmem_cell *boot_error_count; + struct nvmem_cell *panic_count; +}; + +static const char * const nvmem_names[] = { + "shutdown_flag", + "boot_stage", + "boot_error_count", + "panic_count", +}; + +enum boot_stage { + BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */ + BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */ + BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */ +}; + +struct macsmc_reboot { + struct device *dev; + struct apple_smc *smc; + struct notifier_block reboot_notify; + + union { + struct macsmc_reboot_nvmem nvm; + struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)]; + }; +}; + +/* Helpers to read/write a u8 given a struct nvmem_cell */ +static int nvmem_cell_get_u8(struct nvmem_cell *cell) +{ + size_t len; + void *bfr; + u8 val; + + bfr = nvmem_cell_read(cell, &len); + if (IS_ERR(bfr)) + return PTR_ERR(bfr); + + if (len < 1) { + kfree(bfr); + return -EINVAL; + } + + val = *(u8 *)bfr; + kfree(bfr); + return val; +} + +static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val) +{ + return nvmem_cell_write(cell, &val, sizeof(val)); +} + +/* + * SMC 'MBSE' key actions: + * + * 'offw' - shutdown warning + * 'slpw' - sleep warning + * 'rest' - restart warning + * 'off1' - shutdown (needs PMU bit set to stay on) + * 'susp' - suspend + * 'phra' - restart ("PE Halt Restart Action"?) + * 'panb' - panic beginning + * 'pane' - panic end + */ + +static int macsmc_prepare_atomic(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Preparing SMC for atomic mode\n"); + + apple_smc_enter_atomic(reboot->smc); + return NOTIFY_OK; +} + +static int macsmc_power_off(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing power off (off1)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n"); + } else { + mdelay(100); + WARN_ONCE(1, "Unable to power off system\n"); + } + + return NOTIFY_OK; +} + +static int macsmc_restart(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing restart (phra)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n"); + } else { + mdelay(100); + WARN_ONCE(1, "Unable to restart system\n"); + } + + return NOTIFY_OK; +} + +static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data) +{ + struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify); + u8 shutdown_flag; + u32 val; + + switch (action) { + case SYS_RESTART: + val = SMC_KEY(rest); + shutdown_flag = 0; + break; + case SYS_POWER_OFF: + val = SMC_KEY(offw); + shutdown_flag = 1; + break; + default: + return NOTIFY_DONE; + } + + dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val); + + /* On the Mac Mini, this will turn off the LED for power off */ + if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0) + dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val); + + /* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* + * Set the PMU flag to actually reboot into the off state. + * Without this, the device will just reboot. We make it optional in case it is no longer + * necessary on newer hardware. + */ + if (reboot->nvm.shutdown_flag && + nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0) + dev_err(reboot->dev, "Failed to write shutdown_flag\n"); + + return NOTIFY_OK; +} + +static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot) +{ + int boot_error_count, panic_count; + + if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count) + return; + + boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count); + if (boot_error_count < 0) { + dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count); + return; + } + + panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count); + if (panic_count < 0) { + dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count); + return; + } + + if (!boot_error_count && !panic_count) + return; + + dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n", + boot_error_count, panic_count); + + if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset panic_count\n"); + if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset boot_error_count\n"); +} + +static int macsmc_reboot_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_reboot *reboot; + int ret, i; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + reboot->dev = &pdev->dev; + reboot->smc = smc; + + platform_set_drvdata(pdev, reboot); + + for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) { + struct nvmem_cell *cell; + + cell = devm_nvmem_cell_get(&pdev->dev, + nvmem_names[i]); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n", + nvmem_names[i], PTR_ERR(cell)); + /* Non fatal, we'll deal with it */ + cell = NULL; + } + reboot->nvm_cells[i] = cell; + } + + /* Set the boot_stage to indicate we're running the OS kernel */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* Display and clear the error counts */ + macsmc_power_init_error_counts(reboot); + + reboot->reboot_notify.notifier_call = macsmc_reboot_notify; + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register power-off prepare handler\n"); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH, + macsmc_power_off, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register power-off handler\n"); + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART_PREPARE, + SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register restart prepare handler\n"); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH, + macsmc_restart, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n"); + + ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n"); + + dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n"); + + return 0; +} + +static const struct of_device_id macsmc_reboot_of_table[] = { + { .compatible = "apple,smc-reboot", }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_reboot_of_table); + +static struct platform_driver macsmc_reboot_driver = { + .driver = { + .name = "macsmc-reboot", + .of_match_table = macsmc_reboot_of_table, + }, + .probe = macsmc_reboot_probe, +}; +module_platform_driver(macsmc_reboot_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver"); +MODULE_AUTHOR("Hector Martin "); -- GitLab From c32f66d17455970091bd97d7a9cac6f38dfcc423 Mon Sep 17 00:00:00 2001 From: Andrei Lalaev Date: Mon, 21 Jul 2025 17:57:37 +0200 Subject: [PATCH 849/868] gpiolib: of: add forward declaration for struct device_node Commit 08a149c40bdb ("gpiolib: Clean up headers") added a forward declaration for struct device. Later, commit 07445ae1c263 ("gpiolib: of: change of_find_gpio() to accept device node") changed the function signature to accept a struct device_node instead of a struct device. Replace forward declaration of struct device with struct device_node to match the updated function signature. Signed-off-by: Andrei Lalaev Link: https://lore.kernel.org/r/20250721155737.261990-1-andrey.lalaev@gmail.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 3eebfac290c57..2257f7a498a10 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -8,7 +8,7 @@ #include -struct device; +struct device_node; struct fwnode_handle; struct gpio_chip; -- GitLab From cc2f156a33278d9b23b5cf8f738c55c842d0f225 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Wed, 23 Jul 2025 08:56:43 +0000 Subject: [PATCH 850/868] dt-bindings: gpio: rockchip: Allow use of a power-domain The GPIO controllers in most Rockchip SoCs are part of power domains that are always powered on, i.e. PD_BUS or PD_PMU. These always powered on power domains have typically not been described in the device tree. Because these power domains have been left out of the device tree there has not been any real need to properly describe the GPIO controllers power domain. On RK3528 the GPIO controllers are spread out among the described PD_RKVENC, PD_VO and PD_VPU power domains. However, one GPIO controller belong to an undescribed always powered on power domain. Add support to describe an optional power-domains for the GPIO controllers in Rockchip SoCs. Signed-off-by: Jonas Karlman Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250723085654.2273324-2-jonas@kwiboo.se Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index d76987ce8e50e..bdd83f42615cb 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -41,6 +41,9 @@ properties: "#interrupt-cells": const: 2 + power-domains: + maxItems: 1 + patternProperties: "^.+-hog(-[0-9]+)?$": type: object -- GitLab From 71d141edbfa3e0a213c537e979790835550270d6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Jul 2025 12:31:12 +0100 Subject: [PATCH 851/868] regulator: Kconfig: Fix spelling mistake "regualtor" -> "regulator" There is a spelling mistake in the REGULATOR_RT4803 config. Fix it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250724113113.143009-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7423954153b07..eaa6df1c9f806 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1203,7 +1203,7 @@ config REGULATOR_RT4801 The device supports two regulators (DSVP/DSVN). config REGULATOR_RT4803 - tristate "Richtek RT4803 boost regualtor" + tristate "Richtek RT4803 boost regulator" depends on I2C select REGMAP_I2C help -- GitLab From 7438379cfc47046f7507dfdb54906acf56288b9f Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:43 +0800 Subject: [PATCH 852/868] spi: dt-bindings: spi-sg2044-nor: Change SOPHGO SG2042 With further testing, directly using the spi-sg2044-nor driver on SG2042 does not work. SG2042 is found to lack full compatibility with SG2044. SG2044 has OPT register and it's necessary to write but SG2042 does not. Due to other possible hardware detail differences, it is better to bind SG2042 independently. Fixes: 8450f1e0d3d0 ("spi: dt-bindings: spi-sg2044-nor: Add SOPHGO SG2042") Signed-off-by: Zixian Zeng Acked-by: Rob Herring (Arm) Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-1-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sg2044-nor.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml index 66e54dedab140..0e7ead7637052 100644 --- a/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml +++ b/Documentation/devicetree/bindings/spi/spi-sg2044-nor.yaml @@ -14,12 +14,9 @@ allOf: properties: compatible: - oneOf: - - const: sophgo,sg2044-spifmc-nor - - items: - - enum: - - sophgo,sg2042-spifmc-nor - - const: sophgo,sg2044-spifmc-nor + enum: + - sophgo,sg2042-spifmc-nor + - sophgo,sg2044-spifmc-nor reg: maxItems: 1 -- GitLab From 5653b4f8840801d9d141ba6a6c10e7cc15040c5b Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:44 +0800 Subject: [PATCH 853/868] spi: spi-sg2044-nor: Add configurable chip_info SG2044 and SG2042 have similar SPI-NOR flash controller design, but have incompatibility which causes existing driver not working on SG2042: 1. SPI-NOR flash controller on SG2042 have no OPT register. 2. FIFO trigger level on SG2042 should be strictly less than 8. So introduce a new configurable chip_info structure to hold the different configuration. Link: https://github.com/sophgo/sophgo-doc/blob/main/SG2042/TRM/source/SPI-flash.rst Signed-off-by: Zixian Zeng Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-2-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index a59aa3fc55d27..0ef569eb28b76 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -84,12 +84,18 @@ #define SPIFMC_MAX_READ_SIZE 0x10000 +struct sg204x_spifmc_chip_info { + bool has_opt_reg; + u32 rd_fifo_int_trigger_level; +}; + struct sg2044_spifmc { struct spi_controller *ctrl; void __iomem *io_base; struct device *dev; struct mutex lock; struct clk *clk; + const struct sg204x_spifmc_chip_info *chip_info; }; static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type) @@ -139,7 +145,7 @@ static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc, reg = sg2044_spifmc_init_reg(spifmc); reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; - reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE; + reg |= spifmc->chip_info->rd_fifo_int_trigger_level; reg |= SPIFMC_TRAN_CSR_WITH_CMD; reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; @@ -335,7 +341,8 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX; - writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); + if (spifmc->chip_info->has_opt_reg) + writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); } else { /* * If write values to the Status Register, @@ -457,6 +464,11 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) ret = devm_mutex_init(dev, &spifmc->lock); if (ret) return ret; + spifmc->chip_info = device_get_match_data(&pdev->dev); + if (!spifmc->chip_info) { + dev_err(&pdev->dev, "Failed to get specific chip info\n"); + return -EINVAL; + } sg2044_spifmc_init(spifmc); sg2044_spifmc_init_reg(spifmc); @@ -468,8 +480,13 @@ static int sg2044_spifmc_probe(struct platform_device *pdev) return 0; } +static const struct sg204x_spifmc_chip_info sg2044_chip_info = { + .has_opt_reg = true, + .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE, +}; + static const struct of_device_id sg2044_spifmc_match[] = { - { .compatible = "sophgo,sg2044-spifmc-nor" }, + { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sg2044_spifmc_match); -- GitLab From f6b159431697c903da1418e70c825faa0cddbdae Mon Sep 17 00:00:00 2001 From: Zixian Zeng Date: Sun, 20 Jul 2025 16:31:45 +0800 Subject: [PATCH 854/868] spi: spi-sg2044-nor: Add SPI-NOR controller for SG2042 Add support for SOPHGO SG2042 SPI-NOR flash controller. Signed-off-by: Zixian Zeng Reviewed-by: Chen Wang & Tested-by: Chen Wang Link: https://patch.msgid.link/20250720-sfg-spifmc-v4-3-033188ad801e@gmail.com Reviewed-by: Chen Wang & Tested-by: Chen Wang Signed-off-by: Mark Brown --- drivers/spi/spi-sg2044-nor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index 0ef569eb28b76..af48b1fcda930 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -485,8 +485,14 @@ static const struct sg204x_spifmc_chip_info sg2044_chip_info = { .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE, }; +static const struct sg204x_spifmc_chip_info sg2042_chip_info = { + .has_opt_reg = false, + .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE, +}; + static const struct of_device_id sg2044_spifmc_match[] = { { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info }, + { .compatible = "sophgo,sg2042-spifmc-nor", .data = &sg2042_chip_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sg2044_spifmc_match); -- GitLab From 78d35a20783941c8ba5cf912349728c6e1bee84b Mon Sep 17 00:00:00 2001 From: Sunny Luo Date: Fri, 18 Jul 2025 09:52:16 +0800 Subject: [PATCH 855/868] spi: dt-bindings: Add binding document of Amlogic SPISG controller The SPISG is a new communication oriented SPI controller of Amlogic, which supports PIO, block DMA and scatter-gather DMA three operation modes. Signed-off-by: Sunny Luo Acked-by: Conor Dooley Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-1-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- .../bindings/spi/amlogic,a4-spisg.yaml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml diff --git a/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml new file mode 100644 index 0000000000000..9bfb8089f7ea2 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Amlogic, Inc. All rights reserved +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/amlogic,a4-spisg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic SPI Scatter-Gather Controller + +maintainers: + - Xianwei Zhao + - Sunny Luo + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: amlogic,a4-spisg + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: pclk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + spi@50000 { + compatible = "amlogic,a4-spisg"; + reg = <0x50000 0x38>; + interrupts = ; + clocks = <&clkc 37>, + <&clkc 93>; + clock-names = "core", "pclk"; + #address-cells = <1>; + #size-cells = <0>; + }; -- GitLab From cef9991e04aed3305c61c392e880f6e01a0c2ea4 Mon Sep 17 00:00:00 2001 From: Sunny Luo Date: Fri, 18 Jul 2025 09:52:17 +0800 Subject: [PATCH 856/868] spi: Add Amlogic SPISG driver Introduced support for the new SPI IP (SPISG) driver. The SPISG is a communication-oriented SPI controller from Amlogic,supporting three operation modes: PIO, block DMA, and scatter-gather DMA. Due to there is no FIFO, PIO mode can only transfer one word at a time, which is extremely slow. Therefore, this mode was not implemented. Signed-off-by: Sunny Luo Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-2-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-amlogic-spisg.c | 888 ++++++++++++++++++++++++++++++++ 3 files changed, 898 insertions(+) create mode 100644 drivers/spi/spi-amlogic-spisg.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc36049..e11341df2ecf4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -99,6 +99,15 @@ config SPI_AMLOGIC_SPIFC_A1 This enables master mode support for the SPIFC (SPI flash controller) available in Amlogic A1 (A113L SoC). +config SPI_AMLOGIC_SPISG + tristate "Amlogic SPISG controller" + depends on COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + help + This enables master mode support for the SPISG (SPI scatter-gather + communication controller), which is available on platforms such as + Amlogic A4 SoCs. + config SPI_APPLE tristate "Apple SoC SPI Controller platform driver" depends on ARCH_APPLE || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc5316..b74e3104d71f1 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o +obj-$(CONFIG_SPI_AMLOGIC_SPISG) += spi-amlogic-spisg.o obj-$(CONFIG_SPI_APPLE) += spi-apple.o obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c new file mode 100644 index 0000000000000..48a5325b4db7b --- /dev/null +++ b/drivers/spi/spi-amlogic-spisg.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Amlogic SPI communication Scatter-Gather Controller + * + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + * + * Author: Sunny Luo + * Author: Xianwei Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Map */ +#define SPISG_REG_CFG_READY 0x00 + +#define SPISG_REG_CFG_SPI 0x04 +#define CFG_BUS64_EN BIT(0) +#define CFG_SLAVE_EN BIT(1) +#define CFG_SLAVE_SELECT GENMASK(3, 2) +#define CFG_SFLASH_WP BIT(4) +#define CFG_SFLASH_HD BIT(5) +/* start on vsync rising */ +#define CFG_HW_POS BIT(6) +/* start on vsync falling */ +#define CFG_HW_NEG BIT(7) + +#define SPISG_REG_CFG_START 0x08 +#define CFG_BLOCK_NUM GENMASK(19, 0) +#define CFG_BLOCK_SIZE GENMASK(22, 20) +#define CFG_DATA_COMMAND BIT(23) +#define CFG_OP_MODE GENMASK(25, 24) +#define CFG_RXD_MODE GENMASK(27, 26) +#define CFG_TXD_MODE GENMASK(29, 28) +#define CFG_EOC BIT(30) +#define CFG_PEND BIT(31) + +#define SPISG_REG_CFG_BUS 0x0C +#define CFG_CLK_DIV GENMASK(7, 0) +#define CLK_DIV_WIDTH 8 +#define CFG_RX_TUNING GENMASK(11, 8) +#define CFG_TX_TUNING GENMASK(15, 12) +#define CFG_CS_SETUP GENMASK(19, 16) +#define CFG_LANE GENMASK(21, 20) +#define CFG_HALF_DUPLEX BIT(22) +#define CFG_B_L_ENDIAN BIT(23) +#define CFG_DC_MODE BIT(24) +#define CFG_NULL_CTL BIT(25) +#define CFG_DUMMY_CTL BIT(26) +#define CFG_READ_TURN GENMASK(28, 27) +#define CFG_KEEP_SS BIT(29) +#define CFG_CPHA BIT(30) +#define CFG_CPOL BIT(31) + +#define SPISG_REG_PIO_TX_DATA_L 0x10 +#define SPISG_REG_PIO_TX_DATA_H 0x14 +#define SPISG_REG_PIO_RX_DATA_L 0x18 +#define SPISG_REG_PIO_RX_DATA_H 0x1C +#define SPISG_REG_MEM_TX_ADDR_L 0x10 +#define SPISG_REG_MEM_TX_ADDR_H 0x14 +#define SPISG_REG_MEM_RX_ADDR_L 0x18 +#define SPISG_REG_MEM_RX_ADDR_H 0x1C +#define SPISG_REG_DESC_LIST_L 0x20 +#define SPISG_REG_DESC_LIST_H 0x24 +#define LIST_DESC_PENDING BIT(31) +#define SPISG_REG_DESC_CURRENT_L 0x28 +#define SPISG_REG_DESC_CURRENT_H 0x2c +#define SPISG_REG_IRQ_STS 0x30 +#define SPISG_REG_IRQ_ENABLE 0x34 +#define IRQ_RCH_DESC_EOC BIT(0) +#define IRQ_RCH_DESC_INVALID BIT(1) +#define IRQ_RCH_DESC_RESP BIT(2) +#define IRQ_RCH_DATA_RESP BIT(3) +#define IRQ_WCH_DESC_EOC BIT(4) +#define IRQ_WCH_DESC_INVALID BIT(5) +#define IRQ_WCH_DESC_RESP BIT(6) +#define IRQ_WCH_DATA_RESP BIT(7) +#define IRQ_DESC_ERR BIT(8) +#define IRQ_SPI_READY BIT(9) +#define IRQ_DESC_DONE BIT(10) +#define IRQ_DESC_CHAIN_DONE BIT(11) + +#define SPISG_MAX_REG 0x40 + +#define SPISG_BLOCK_MAX 0x100000 + +#define SPISG_OP_MODE_WRITE_CMD 0 +#define SPISG_OP_MODE_READ_STS 1 +#define SPISG_OP_MODE_WRITE 2 +#define SPISG_OP_MODE_READ 3 + +#define SPISG_DATA_MODE_NONE 0 +#define SPISG_DATA_MODE_PIO 1 +#define SPISG_DATA_MODE_MEM 2 +#define SPISG_DATA_MODE_SG 3 + +#define SPISG_CLK_DIV_MAX 256 +/* recommended by specification */ +#define SPISG_CLK_DIV_MIN 4 +#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1) + +#define SPISG_PCLK_RATE_MIN 24000000 + +#define SPISG_SINGLE_SPI 0 +#define SPISG_DUAL_SPI 1 +#define SPISG_QUAD_SPI 2 + +struct spisg_sg_link { +#define LINK_ADDR_VALID BIT(0) +#define LINK_ADDR_EOC BIT(1) +#define LINK_ADDR_IRQ BIT(2) +#define LINK_ADDR_ACT GENMASK(5, 3) +#define LINK_ADDR_RING BIT(6) +#define LINK_ADDR_LEN GENMASK(31, 8) + u32 addr; + u32 addr1; +}; + +struct spisg_descriptor { + u32 cfg_start; + u32 cfg_bus; + u64 tx_paddr; + u64 rx_paddr; +}; + +struct spisg_descriptor_extra { + struct spisg_sg_link *tx_ccsg; + struct spisg_sg_link *rx_ccsg; + int tx_ccsg_len; + int rx_ccsg_len; +}; + +struct spisg_device { + struct spi_controller *controller; + struct platform_device *pdev; + struct regmap *map; + struct clk *core; + struct clk *pclk; + struct clk *sclk; + struct clk_div_table *tbl; + struct completion completion; + u32 status; + u32 speed_hz; + u32 effective_speed_hz; + u32 bytes_per_word; + u32 cfg_spi; + u32 cfg_start; + u32 cfg_bus; +}; + +static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay) +{ + u32 ns; + + if (!delay) + return 0; + + if (delay->unit == SPI_DELAY_UNIT_SCK) + return delay->value; + + ns = spi_delay_to_ns(delay, NULL); + if (ns < 0) + return 0; + + return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC); +} + +static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg) +{ + u32 ret; + + regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret); + if (ret) + regmap_write(spisg->map, SPISG_REG_CFG_READY, 0); + + return ret; +} + +static inline void aml_spisg_sem_up_write(struct spisg_device *spisg) +{ + regmap_write(spisg->map, SPISG_REG_CFG_READY, 1); +} + +static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz) +{ + u32 cfg_bus; + + if (!speed_hz || speed_hz == spisg->speed_hz) + return 0; + + spisg->speed_hz = speed_hz; + clk_set_rate(spisg->sclk, speed_hz); + /* Store the div for the descriptor mode */ + regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus); + spisg->cfg_bus &= ~CFG_CLK_DIV; + spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV; + spisg->effective_speed_hz = clk_get_rate(spisg->sclk); + dev_dbg(&spisg->pdev->dev, + "desired speed %dHz, effective speed %dHz\n", + speed_hz, spisg->effective_speed_hz); + + return 0; +} + +static bool aml_spisg_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + return true; +} + +static void aml_spisg_sg_xlate(struct sg_table *sgt, struct spisg_sg_link *ccsg) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + ccsg->addr = FIELD_PREP(LINK_ADDR_VALID, 1) | + FIELD_PREP(LINK_ADDR_RING, 0) | + FIELD_PREP(LINK_ADDR_EOC, sg_is_last(sg)) | + FIELD_PREP(LINK_ADDR_LEN, sg_dma_len(sg)); + ccsg->addr1 = (u32)sg_dma_address(sg); + ccsg++; + } +} + +static int nbits_to_lane[] = { + SPISG_SINGLE_SPI, + SPISG_SINGLE_SPI, + SPISG_DUAL_SPI, + -EINVAL, + SPISG_QUAD_SPI +}; + +static int aml_spisg_setup_transfer(struct spisg_device *spisg, + struct spi_transfer *xfer, + struct spisg_descriptor *desc, + struct spisg_descriptor_extra *exdesc) +{ + int block_size, blocks; + struct device *dev = &spisg->pdev->dev; + struct spisg_sg_link *ccsg; + int ccsg_len; + dma_addr_t paddr; + int ret; + + memset(desc, 0, sizeof(*desc)); + if (exdesc) + memset(exdesc, 0, sizeof(*exdesc)); + aml_spisg_set_speed(spisg, xfer->speed_hz); + xfer->effective_speed_hz = spisg->effective_speed_hz; + + desc->cfg_start = spisg->cfg_start; + desc->cfg_bus = spisg->cfg_bus; + + block_size = xfer->bits_per_word >> 3; + blocks = xfer->len / block_size; + + desc->cfg_start |= FIELD_PREP(CFG_EOC, 0); + desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, !xfer->cs_change); + desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 0); + + if (xfer->tx_buf || xfer->tx_dma) { + desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]); + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE); + } + if (xfer->rx_buf || xfer->rx_dma) { + desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]); + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ); + } + + if (FIELD_GET(CFG_OP_MODE, desc->cfg_start) == SPISG_OP_MODE_READ_STS) { + desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, blocks) | + FIELD_PREP(CFG_BLOCK_NUM, 1); + } else { + blocks = min_t(int, blocks, SPISG_BLOCK_MAX); + desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, block_size & 0x7) | + FIELD_PREP(CFG_BLOCK_NUM, blocks); + } + + if (xfer->tx_sg.nents && xfer->tx_sg.sgl) { + ccsg_len = xfer->tx_sg.nents * sizeof(struct spisg_sg_link); + ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA); + if (!ccsg) { + dev_err(dev, "alloc tx_ccsg failed\n"); + return -ENOMEM; + } + + aml_spisg_sg_xlate(&xfer->tx_sg, ccsg); + paddr = dma_map_single(dev, (void *)ccsg, + ccsg_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + kfree(ccsg); + dev_err(dev, "tx ccsg map failed\n"); + return ret; + } + + desc->tx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_SG); + exdesc->tx_ccsg = ccsg; + exdesc->tx_ccsg_len = ccsg_len; + dma_sync_sgtable_for_device(spisg->controller->cur_tx_dma_dev, + &xfer->tx_sg, DMA_TO_DEVICE); + } else if (xfer->tx_buf || xfer->tx_dma) { + paddr = xfer->tx_dma; + if (!paddr) { + paddr = dma_map_single(dev, (void *)xfer->tx_buf, + xfer->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + dev_err(dev, "tx buf map failed\n"); + return ret; + } + } + desc->tx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_MEM); + } + + if (xfer->rx_sg.nents && xfer->rx_sg.sgl) { + ccsg_len = xfer->rx_sg.nents * sizeof(struct spisg_sg_link); + ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA); + if (!ccsg) { + dev_err(dev, "alloc rx_ccsg failed\n"); + return -ENOMEM; + } + + aml_spisg_sg_xlate(&xfer->rx_sg, ccsg); + paddr = dma_map_single(dev, (void *)ccsg, + ccsg_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + kfree(ccsg); + dev_err(dev, "rx ccsg map failed\n"); + return ret; + } + + desc->rx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_SG); + exdesc->rx_ccsg = ccsg; + exdesc->rx_ccsg_len = ccsg_len; + dma_sync_sgtable_for_device(spisg->controller->cur_rx_dma_dev, + &xfer->rx_sg, DMA_FROM_DEVICE); + } else if (xfer->rx_buf || xfer->rx_dma) { + paddr = xfer->rx_dma; + if (!paddr) { + paddr = dma_map_single(dev, xfer->rx_buf, + xfer->len, DMA_FROM_DEVICE); + ret = dma_mapping_error(dev, paddr); + if (ret) { + dev_err(dev, "rx buf map failed\n"); + return ret; + } + } + + desc->rx_paddr = paddr; + desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_MEM); + } + + return 0; +} + +static void aml_spisg_cleanup_transfer(struct spisg_device *spisg, + struct spi_transfer *xfer, + struct spisg_descriptor *desc, + struct spisg_descriptor_extra *exdesc) +{ + struct device *dev = &spisg->pdev->dev; + + if (desc->tx_paddr) { + if (FIELD_GET(CFG_TXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) { + dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr, + exdesc->tx_ccsg_len, DMA_TO_DEVICE); + kfree(exdesc->tx_ccsg); + dma_sync_sgtable_for_cpu(spisg->controller->cur_tx_dma_dev, + &xfer->tx_sg, DMA_TO_DEVICE); + } else if (!xfer->tx_dma) { + dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr, + xfer->len, DMA_TO_DEVICE); + } + } + + if (desc->rx_paddr) { + if (FIELD_GET(CFG_RXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) { + dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr, + exdesc->rx_ccsg_len, DMA_TO_DEVICE); + kfree(exdesc->rx_ccsg); + dma_sync_sgtable_for_cpu(spisg->controller->cur_rx_dma_dev, + &xfer->rx_sg, DMA_FROM_DEVICE); + } else if (!xfer->rx_dma) { + dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr, + xfer->len, DMA_FROM_DEVICE); + } + } +} + +static void aml_spisg_setup_null_desc(struct spisg_device *spisg, + struct spisg_descriptor *desc, + u32 n_sclk) +{ + /* unit is the last xfer sclk */ + desc->cfg_start = spisg->cfg_start; + desc->cfg_bus = spisg->cfg_bus; + + desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE) | + FIELD_PREP(CFG_BLOCK_SIZE, 1) | + FIELD_PREP(CFG_BLOCK_NUM, DIV_ROUND_UP(n_sclk, 8)); + + desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 1); +} + +static void aml_spisg_pending(struct spisg_device *spisg, + dma_addr_t desc_paddr, + bool trig, + bool irq_en) +{ + u32 desc_l, desc_h, cfg_spi, irq_enable; + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc_l = (u64)desc_paddr & 0xffffffff; + desc_h = (u64)desc_paddr >> 32; +#else + desc_l = desc_paddr & 0xffffffff; + desc_h = 0; +#endif + + cfg_spi = spisg->cfg_spi; + if (trig) + cfg_spi |= CFG_HW_POS; + else + desc_h |= LIST_DESC_PENDING; + + irq_enable = IRQ_RCH_DESC_INVALID | IRQ_RCH_DESC_RESP | + IRQ_RCH_DATA_RESP | IRQ_WCH_DESC_INVALID | + IRQ_WCH_DESC_RESP | IRQ_WCH_DATA_RESP | + IRQ_DESC_ERR | IRQ_DESC_CHAIN_DONE; + regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? irq_enable : 0); + regmap_write(spisg->map, SPISG_REG_CFG_SPI, cfg_spi); + regmap_write(spisg->map, SPISG_REG_DESC_LIST_L, desc_l); + regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, desc_h); +} + +static irqreturn_t aml_spisg_irq(int irq, void *data) +{ + struct spisg_device *spisg = (void *)data; + u32 sts; + + spisg->status = 0; + regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts); + regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts); + if (sts & (IRQ_RCH_DESC_INVALID | + IRQ_RCH_DESC_RESP | + IRQ_RCH_DATA_RESP | + IRQ_WCH_DESC_INVALID | + IRQ_WCH_DESC_RESP | + IRQ_WCH_DATA_RESP | + IRQ_DESC_ERR)) + spisg->status = sts; + else if (sts & IRQ_DESC_CHAIN_DONE) + spisg->status = 0; + else + return IRQ_NONE; + + complete(&spisg->completion); + + return IRQ_HANDLED; +} + +static int aml_spisg_transfer_one_message(struct spi_controller *ctlr, + struct spi_message *msg) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + struct device *dev = &spisg->pdev->dev; + unsigned long long ms = 0; + struct spi_transfer *xfer; + struct spisg_descriptor *descs, *desc; + struct spisg_descriptor_extra *exdescs, *exdesc; + dma_addr_t descs_paddr; + int desc_num = 1, descs_len; + u32 cs_hold_in_sclk = 0; + int ret = -EIO; + + if (!aml_spisg_sem_down_read(spisg)) { + spi_finalize_current_message(ctlr); + dev_err(dev, "controller busy\n"); + return -EBUSY; + } + + /* calculate the desc num for all xfer */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) + desc_num++; + + /* alloc descriptor/extra-descriptor table */ + descs = kcalloc(desc_num, sizeof(*desc) + sizeof(*exdesc), + GFP_KERNEL | GFP_DMA); + if (!descs) { + spi_finalize_current_message(ctlr); + aml_spisg_sem_up_write(spisg); + return -ENOMEM; + } + descs_len = sizeof(*desc) * desc_num; + exdescs = (struct spisg_descriptor_extra *)(descs + desc_num); + + /* config descriptor for each xfer */ + desc = descs; + exdesc = exdescs; + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + ret = aml_spisg_setup_transfer(spisg, xfer, desc, exdesc); + if (ret) { + dev_err(dev, "config descriptor failed\n"); + goto end; + } + + /* calculate cs-setup delay with the first xfer speed */ + if (list_is_first(&xfer->transfer_list, &msg->transfers)) + desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP, + spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup)); + + /* calculate cs-hold delay with the last xfer speed */ + if (list_is_last(&xfer->transfer_list, &msg->transfers)) + cs_hold_in_sclk = + spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold); + + desc++; + exdesc++; + ms += DIV_ROUND_UP_ULL(8LL * MSEC_PER_SEC * xfer->len, + xfer->effective_speed_hz); + } + + if (cs_hold_in_sclk) + /* additional null-descriptor to achieve the cs-hold delay */ + aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk); + else + desc--; + + desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0); + desc->cfg_start |= FIELD_PREP(CFG_EOC, 1); + + /* some tolerances */ + ms += ms + 20; + if (ms > UINT_MAX) + ms = UINT_MAX; + + descs_paddr = dma_map_single(dev, (void *)descs, + descs_len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, descs_paddr); + if (ret) { + dev_err(dev, "desc table map failed\n"); + goto end; + } + + reinit_completion(&spisg->completion); + aml_spisg_pending(spisg, descs_paddr, false, true); + if (wait_for_completion_timeout(&spisg->completion, + spi_controller_is_target(spisg->controller) ? + MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms))) + ret = spisg->status ? -EIO : 0; + else + ret = -ETIMEDOUT; + + dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE); +end: + desc = descs; + exdesc = exdescs; + list_for_each_entry(xfer, &msg->transfers, transfer_list) + aml_spisg_cleanup_transfer(spisg, xfer, desc++, exdesc++); + kfree(descs); + + if (!ret) + msg->actual_length = msg->frame_length; + msg->status = ret; + spi_finalize_current_message(ctlr); + aml_spisg_sem_up_write(spisg); + + return ret; +} + +static int aml_spisg_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + struct spi_device *spi = message->spi; + + if (!spi->bits_per_word || spi->bits_per_word % 8) { + dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word); + return -EINVAL; + } + + spisg->bytes_per_word = spi->bits_per_word >> 3; + + spisg->cfg_spi &= ~CFG_SLAVE_SELECT; + spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0)); + + spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX); + spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) | + FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) | + FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) | + FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE)); + + return 0; +} + +static int aml_spisg_setup(struct spi_device *spi) +{ + if (!spi->controller_state) + spi->controller_state = spi_controller_get_devdata(spi->controller); + + return 0; +} + +static void aml_spisg_cleanup(struct spi_device *spi) +{ + spi->controller_state = NULL; +} + +static int aml_spisg_target_abort(struct spi_controller *ctlr) +{ + struct spisg_device *spisg = spi_controller_get_devdata(ctlr); + + spisg->status = 0; + regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0); + complete(&spisg->completion); + + return 0; +} + +static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base) +{ + struct device *dev = &spisg->pdev->dev; + struct clk_init_data init; + struct clk_divider *div; + struct clk_div_table *tbl; + char name[32]; + int ret, i; + + spisg->core = devm_clk_get_enabled(dev, "core"); + if (IS_ERR_OR_NULL(spisg->core)) { + dev_err(dev, "core clock request failed\n"); + return PTR_ERR(spisg->core); + } + + spisg->pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR_OR_NULL(spisg->pclk)) { + dev_err(dev, "pclk clock request failed\n"); + return PTR_ERR(spisg->pclk); + } + + clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN); + + clk_disable_unprepare(spisg->pclk); + + tbl = devm_kzalloc(dev, sizeof(struct clk_div_table) * (DIV_NUM + 1), GFP_KERNEL); + if (!tbl) + return -ENOMEM; + + for (i = 0; i < DIV_NUM; i++) { + tbl[i].val = i + SPISG_CLK_DIV_MIN - 1; + tbl[i].div = i + SPISG_CLK_DIV_MIN; + } + spisg->tbl = tbl; + + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return -ENOMEM; + + div->flags = CLK_DIVIDER_ROUND_CLOSEST; + div->reg = base + SPISG_REG_CFG_BUS; + div->shift = __bf_shf(CFG_CLK_DIV); + div->width = CLK_DIV_WIDTH; + div->table = tbl; + + /* Register value should not be outside of the table */ + regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV, + FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1)); + + /* Register clk-divider */ + snprintf(name, sizeof(name), "%s_div", dev_name(dev)); + init.name = name; + init.ops = &clk_divider_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_data = &(const struct clk_parent_data) { + .fw_name = "pclk", + }; + init.num_parents = 1; + div->hw.init = &init; + ret = devm_clk_hw_register(dev, &div->hw); + if (ret) { + dev_err(dev, "clock registration failed\n"); + return ret; + } + + spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL); + if (IS_ERR_OR_NULL(spisg->sclk)) { + dev_err(dev, "get clock failed\n"); + return PTR_ERR(spisg->sclk); + } + + clk_prepare_enable(spisg->sclk); + + return 0; +} + +static int aml_spisg_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct spisg_device *spisg; + struct device *dev = &pdev->dev; + void __iomem *base; + int ret, irq; + + const struct regmap_config aml_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPISG_MAX_REG, + }; + + if (of_property_read_bool(dev->of_node, "spi-slave")) + ctlr = spi_alloc_target(dev, sizeof(*spisg)); + else + ctlr = spi_alloc_host(dev, sizeof(*spisg)); + if (!ctlr) + return dev_err_probe(dev, -ENOMEM, "controller allocation failed\n"); + + spisg = spi_controller_get_devdata(ctlr); + spisg->controller = ctlr; + + spisg->pdev = pdev; + platform_set_drvdata(pdev, spisg); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n"); + + spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config); + if (IS_ERR(spisg->map)) + return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto out_controller; + } + + ret = device_reset_optional(dev); + if (ret) + return dev_err_probe(dev, ret, "reset dev failed\n"); + + ret = aml_spisg_clk_init(spisg, base); + if (ret) + return dev_err_probe(dev, ret, "clock init failed\n"); + + spisg->cfg_spi = 0; + spisg->cfg_start = 0; + spisg->cfg_bus = 0; + + spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) | + FIELD_PREP(CFG_SFLASH_HD, 1); + if (spi_controller_is_target(ctlr)) { + spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1); + spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf); + } + /* default pending */ + spisg->cfg_start = FIELD_PREP(CFG_PEND, 1); + + pm_runtime_set_active(&spisg->pdev->dev); + pm_runtime_enable(&spisg->pdev->dev); + pm_runtime_resume_and_get(&spisg->pdev->dev); + + ctlr->num_chipselect = 4; + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST | + SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD; + ctlr->max_speed_hz = 1000 * 1000 * 100; + ctlr->min_speed_hz = 1000 * 10; + ctlr->setup = aml_spisg_setup; + ctlr->cleanup = aml_spisg_cleanup; + ctlr->prepare_message = aml_spisg_prepare_message; + ctlr->transfer_one_message = aml_spisg_transfer_one_message; + ctlr->target_abort = aml_spisg_target_abort; + ctlr->can_dma = aml_spisg_can_dma; + ctlr->max_dma_len = SPISG_BLOCK_MAX; + ctlr->auto_runtime_pm = true; + + dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX); + + ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg); + if (ret) { + dev_err(&pdev->dev, "irq request failed\n"); + goto out_clk; + } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) { + dev_err(&pdev->dev, "spi controller registration failed\n"); + goto out_clk; + } + + init_completion(&spisg->completion); + + pm_runtime_put(&spisg->pdev->dev); + + return 0; +out_clk: + if (spisg->core) + clk_disable_unprepare(spisg->core); + clk_disable_unprepare(spisg->pclk); +out_controller: + spi_controller_put(ctlr); + + return ret; +} + +static void aml_spisg_remove(struct platform_device *pdev) +{ + struct spisg_device *spisg = platform_get_drvdata(pdev); + + if (!pm_runtime_suspended(&pdev->dev)) { + pinctrl_pm_select_sleep_state(&spisg->pdev->dev); + clk_disable_unprepare(spisg->core); + clk_disable_unprepare(spisg->pclk); + } +} + +static int spisg_suspend_runtime(struct device *dev) +{ + struct spisg_device *spisg = dev_get_drvdata(dev); + + pinctrl_pm_select_sleep_state(&spisg->pdev->dev); + clk_disable_unprepare(spisg->sclk); + clk_disable_unprepare(spisg->core); + + return 0; +} + +static int spisg_resume_runtime(struct device *dev) +{ + struct spisg_device *spisg = dev_get_drvdata(dev); + + clk_prepare_enable(spisg->core); + clk_prepare_enable(spisg->sclk); + pinctrl_pm_select_default_state(&spisg->pdev->dev); + + return 0; +} + +static const struct dev_pm_ops amlogic_spisg_pm_ops = { + .runtime_suspend = spisg_suspend_runtime, + .runtime_resume = spisg_resume_runtime, +}; + +static const struct of_device_id amlogic_spisg_of_match[] = { + { + .compatible = "amlogic,a4-spisg", + }, + + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, amlogic_spisg_of_match); + +static struct platform_driver amlogic_spisg_driver = { + .probe = aml_spisg_probe, + .remove = aml_spisg_remove, + .driver = { + .name = "amlogic-spisg", + .of_match_table = amlogic_spisg_of_match, + .pm = &amlogic_spisg_pm_ops, + }, +}; + +module_platform_driver(amlogic_spisg_driver); + +MODULE_DESCRIPTION("Amlogic SPI Scatter-Gather Controller driver"); +MODULE_AUTHOR("Sunny Luo "); +MODULE_LICENSE("GPL"); -- GitLab From 0ef2a9779e9decee52a85bc393309b3e068a74a6 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Fri, 18 Jul 2025 09:52:18 +0800 Subject: [PATCH 857/868] MAINTAINERS: Add an entry for Amlogic spi driver Add Amlogic spi entry to MAINTAINERS to clarify the maintainers. Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250718-spisg-v5-3-b8f0f1eb93a2@amlogic.com Signed-off-by: Mark Brown --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 10850512c1186..f04f5c3e100b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1306,6 +1306,15 @@ S: Maintained F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml F: drivers/rtc/rtc-amlogic-a4.c +AMLOGIC SPISG DRIVER +M: Sunny Luo +M: Xianwei Zhao +L: linux-amlogic@lists.infradead.org +L: linux-spi@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml +F: drivers/spi/spi-amlogic-spisg.c + AMPHENOL CHIPCAP 2 DRIVER M: Javier Carrasco L: linux-hwmon@vger.kernel.org -- GitLab From 44b91d61c505863b8ae90b7094aee5ca0dce808f Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Fri, 4 Jul 2025 17:20:34 +0100 Subject: [PATCH 858/868] spi: dt-bindings: Document the RZ/V2H(P) RSPI Add dt-bindings for the RSPI IP found inside the Renesas RZ/V2H(P) SoC. Signed-off-by: Fabrizio Castro Reviewed-by: Rob Herring (Arm) Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20250704162036.468765-2-fabrizio.castro.jz@renesas.com Signed-off-by: Mark Brown --- .../bindings/spi/renesas,rzv2h-rspi.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml diff --git a/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml new file mode 100644 index 0000000000000..ab27fefc3c3a3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/renesas,rzv2h-rspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI) + +maintainers: + - Fabrizio Castro + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: renesas,r9a09g057-rspi # RZ/V2H(P) + + reg: + maxItems: 1 + + interrupts: + items: + - description: Idle Interrupt + - description: Error Interrupt + - description: Communication End Interrupt + - description: Receive Buffer Full Interrupt + - description: Transmit Buffer Empty Interrupt + + interrupt-names: + items: + - const: idle + - const: error + - const: end + - const: rx + - const: tx + + clocks: + maxItems: 3 + + clock-names: + items: + - const: pclk + - const: pclk_sfr + - const: tclk + + resets: + maxItems: 2 + + reset-names: + items: + - const: presetn + - const: tresetn + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi@12800800 { + compatible = "renesas,r9a09g057-rspi"; + + reg = <0x12800800 0x400>; + interrupts = , + , + , + , + ; + interrupt-names = "idle", "error", "end", "rx", "tx"; + clocks = <&cpg CPG_MOD 0x5a>, + <&cpg CPG_MOD 0x5b>, + <&cpg CPG_MOD 0x5c>; + clock-names = "pclk", "pclk_sfr", "tclk"; + resets = <&cpg 0x7f>, <&cpg 0x80>; + reset-names = "presetn", "tresetn"; + power-domains = <&cpg>; + #address-cells = <1>; + #size-cells = <0>; + }; -- GitLab From 8b61c8919dff080d83415523cd68f2fef03ccfc7 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Fri, 4 Jul 2025 17:20:35 +0100 Subject: [PATCH 859/868] spi: Add driver for the RZ/V2H(P) RSPI IP The Renesas RZ/V2H(P) RSPI IP supports 4-wire and 3-wire serial communications in both host role and target role. It can use a DMA, but the I/O can also be driven by the processor. RX-only, TX-only, and RX-TX operations are available in DMA mode, while in processor I/O mode it only RX-TX operations are supported. Add a driver to support 4-wire serial communications as host role in processor I/O mode. Signed-off-by: Fabrizio Castro Link: https://patch.msgid.link/20250704162036.468765-3-fabrizio.castro.jz@renesas.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi-rzv2h-rspi.c | 466 +++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 drivers/spi/spi-rzv2h-rspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c51da3fc36049..937e441b9410b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -923,6 +923,14 @@ config SPI_RSPI help SPI driver for Renesas RSPI and QSPI blocks. +config SPI_RZV2H_RSPI + tristate "Renesas RZ/V2H RSPI controller" + depends on ARCH_RENESAS || COMPILE_TEST + help + RSPI driver for the Renesas RZ/V2H Serial Peripheral Interface (RSPI). + RSPI supports both SPI host and SPI target roles. This option only + enables the SPI host role. + config SPI_RZV2M_CSI tristate "Renesas RZ/V2M CSI controller" depends on ARCH_RENESAS || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4ea89f6fc5316..c19d02653b8ae 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o +obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c new file mode 100644 index 0000000000000..dcc431ba60a9e --- /dev/null +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Renesas RZ/V2H Renesas Serial Peripheral Interface (RSPI) + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define RSPI_SPDR 0x00 +#define RSPI_SPCR 0x08 +#define RSPI_SSLP 0x10 +#define RSPI_SPBR 0x11 +#define RSPI_SPSCR 0x13 +#define RSPI_SPCMD 0x14 +#define RSPI_SPDCR2 0x44 +#define RSPI_SPSR 0x52 +#define RSPI_SPSRC 0x6a +#define RSPI_SPFCR 0x6c + +/* Register SPCR */ +#define RSPI_SPCR_MSTR BIT(30) +#define RSPI_SPCR_SPRIE BIT(17) +#define RSPI_SPCR_SCKASE BIT(12) +#define RSPI_SPCR_SPE BIT(0) + +/* Register SPBR */ +#define RSPI_SPBR_SPR_MIN 0 +#define RSPI_SPBR_SPR_MAX 255 + +/* Register SPCMD */ +#define RSPI_SPCMD_SSLA GENMASK(25, 24) +#define RSPI_SPCMD_SPB GENMASK(20, 16) +#define RSPI_SPCMD_LSBF BIT(12) +#define RSPI_SPCMD_SSLKP BIT(7) +#define RSPI_SPCMD_BRDV GENMASK(3, 2) +#define RSPI_SPCMD_CPOL BIT(1) +#define RSPI_SPCMD_CPHA BIT(0) + +#define RSPI_SPCMD_BRDV_MIN 0 +#define RSPI_SPCMD_BRDV_MAX 3 + +/* Register SPDCR2 */ +#define RSPI_SPDCR2_TTRG GENMASK(11, 8) +#define RSPI_SPDCR2_RTRG GENMASK(3, 0) +#define RSPI_FIFO_SIZE 16 + +/* Register SPSR */ +#define RSPI_SPSR_SPRF BIT(15) + +/* Register RSPI_SPSRC */ +#define RSPI_SPSRC_CLEAR 0xfd80 + +#define RSPI_RESET_NUM 2 +#define RSPI_CLK_NUM 3 + +struct rzv2h_rspi_priv { + struct reset_control_bulk_data resets[RSPI_RESET_NUM]; + struct spi_controller *controller; + void __iomem *base; + struct clk *tclk; + wait_queue_head_t wait; + unsigned int bytes_per_word; + u32 freq; + u16 status; +}; + +#define RZV2H_RSPI_TX(func, type) \ +static inline void rzv2h_rspi_tx_##type(struct rzv2h_rspi_priv *rspi, \ + const void *txbuf, \ + unsigned int index) { \ + type buf = 0; \ + \ + if (txbuf) \ + buf = ((type *)txbuf)[index]; \ + \ + func(buf, rspi->base + RSPI_SPDR); \ +} + +#define RZV2H_RSPI_RX(func, type) \ +static inline void rzv2h_rspi_rx_##type(struct rzv2h_rspi_priv *rspi, \ + void *rxbuf, \ + unsigned int index) { \ + type buf = func(rspi->base + RSPI_SPDR); \ + \ + if (rxbuf) \ + ((type *)rxbuf)[index] = buf; \ +} + +RZV2H_RSPI_TX(writel, u32) +RZV2H_RSPI_TX(writew, u16) +RZV2H_RSPI_TX(writeb, u8) +RZV2H_RSPI_RX(readl, u32) +RZV2H_RSPI_RX(readw, u16) +RZV2H_RSPI_RX(readl, u8) + +static void rzv2h_rspi_reg_rmw(const struct rzv2h_rspi_priv *rspi, + int reg_offs, u32 bit_mask, u32 value) +{ + u32 tmp; + + value <<= __ffs(bit_mask); + tmp = (readl(rspi->base + reg_offs) & ~bit_mask) | value; + writel(tmp, rspi->base + reg_offs); +} + +static inline void rzv2h_rspi_spe_disable(const struct rzv2h_rspi_priv *rspi) +{ + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 0); +} + +static inline void rzv2h_rspi_spe_enable(const struct rzv2h_rspi_priv *rspi) +{ + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 1); +} + +static inline void rzv2h_rspi_clear_fifos(const struct rzv2h_rspi_priv *rspi) +{ + writeb(1, rspi->base + RSPI_SPFCR); +} + +static inline void rzv2h_rspi_clear_all_irqs(struct rzv2h_rspi_priv *rspi) +{ + writew(RSPI_SPSRC_CLEAR, rspi->base + RSPI_SPSRC); + rspi->status = 0; +} + +static irqreturn_t rzv2h_rx_irq_handler(int irq, void *data) +{ + struct rzv2h_rspi_priv *rspi = data; + + rspi->status = readw(rspi->base + RSPI_SPSR); + wake_up(&rspi->wait); + + return IRQ_HANDLED; +} + +static inline int rzv2h_rspi_wait_for_interrupt(struct rzv2h_rspi_priv *rspi, + u32 wait_mask) +{ + return wait_event_timeout(rspi->wait, (rspi->status & wait_mask), + HZ) == 0 ? -ETIMEDOUT : 0; +} + +static void rzv2h_rspi_send(struct rzv2h_rspi_priv *rspi, const void *txbuf, + unsigned int index) +{ + switch (rspi->bytes_per_word) { + case 4: + rzv2h_rspi_tx_u32(rspi, txbuf, index); + break; + case 2: + rzv2h_rspi_tx_u16(rspi, txbuf, index); + break; + default: + rzv2h_rspi_tx_u8(rspi, txbuf, index); + } +} + +static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *rspi, void *rxbuf, + unsigned int index) +{ + int ret; + + ret = rzv2h_rspi_wait_for_interrupt(rspi, RSPI_SPSR_SPRF); + if (ret) + return ret; + + switch (rspi->bytes_per_word) { + case 4: + rzv2h_rspi_rx_u32(rspi, rxbuf, index); + break; + case 2: + rzv2h_rspi_rx_u16(rspi, rxbuf, index); + break; + default: + rzv2h_rspi_rx_u8(rspi, rxbuf, index); + } + + return 0; +} + +static int rzv2h_rspi_transfer_one(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(controller); + unsigned int words_to_transfer, i; + int ret = 0; + + transfer->effective_speed_hz = rspi->freq; + words_to_transfer = transfer->len / rspi->bytes_per_word; + + for (i = 0; i < words_to_transfer; i++) { + rzv2h_rspi_clear_all_irqs(rspi); + + rzv2h_rspi_send(rspi, transfer->tx_buf, i); + + ret = rzv2h_rspi_receive(rspi, transfer->rx_buf, i); + if (ret) + break; + } + + rzv2h_rspi_clear_all_irqs(rspi); + + if (ret) + transfer->error = SPI_TRANS_FAIL_IO; + + spi_finalize_current_transfer(controller); + + return ret; +} + +static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr, + u8 brdv) +{ + return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv))); +} + +static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +{ + unsigned long tclk_rate; + int spr; + u8 brdv; + + /* + * From the manual: + * Bit rate = f(RSPI_n_TCLK)/(2*(n+1)*2^(N)) + * + * Where: + * * RSPI_n_TCLK is fixed to 200MHz on V2H + * * n = SPR - is RSPI_SPBR.SPR (from 0 to 255) + * * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3) + */ + tclk_rate = clk_get_rate(rspi->tclk); + for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { + spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1))); + spr--; + if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX) + goto clock_found; + } + + return 0; + +clock_found: + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv); + writeb(spr, rspi->base + RSPI_SPBR); + + return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv); +} + +static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr); + const struct spi_device *spi = message->spi; + struct spi_transfer *xfer; + u32 speed_hz = U32_MAX; + u8 bits_per_word; + u32 conf32; + u16 conf16; + + /* Make sure SPCR.SPE is 0 before amending the configuration */ + rzv2h_rspi_spe_disable(rspi); + + /* Configure the device to work in "host" mode */ + conf32 = RSPI_SPCR_MSTR; + + /* Auto-stop function */ + conf32 |= RSPI_SPCR_SCKASE; + + /* SPI receive buffer full interrupt enable */ + conf32 |= RSPI_SPCR_SPRIE; + + writel(conf32, rspi->base + RSPI_SPCR); + + /* Use SPCMD0 only */ + writeb(0x0, rspi->base + RSPI_SPSCR); + + /* Setup mode */ + conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL)); + conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA)); + conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST)); + conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1); + conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0)); + writel(conf32, rspi->base + RSPI_SPCMD); + if (spi->mode & SPI_CS_HIGH) + writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP); + else + writeb(0, rspi->base + RSPI_SSLP); + + /* Setup FIFO thresholds */ + conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1); + conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0); + writew(conf16, rspi->base + RSPI_SPDCR2); + + rzv2h_rspi_clear_fifos(rspi); + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (!xfer->speed_hz) + continue; + + speed_hz = min(xfer->speed_hz, speed_hz); + bits_per_word = xfer->bits_per_word; + } + + if (speed_hz == U32_MAX) + return -EINVAL; + + rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); + rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1); + + rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); + if (!rspi->freq) + return -EINVAL; + + rzv2h_rspi_spe_enable(rspi); + + return 0; +} + +static int rzv2h_rspi_unprepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr); + + rzv2h_rspi_spe_disable(rspi); + + return 0; +} + +static int rzv2h_rspi_probe(struct platform_device *pdev) +{ + struct spi_controller *controller; + struct device *dev = &pdev->dev; + struct rzv2h_rspi_priv *rspi; + struct clk_bulk_data *clks; + unsigned long tclk_rate; + int irq_rx, ret, i; + + controller = devm_spi_alloc_host(dev, sizeof(*rspi)); + if (!controller) + return -ENOMEM; + + rspi = spi_controller_get_devdata(controller); + platform_set_drvdata(pdev, rspi); + + rspi->controller = controller; + + rspi->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rspi->base)) + return PTR_ERR(rspi->base); + + ret = devm_clk_bulk_get_all_enabled(dev, &clks); + if (ret != RSPI_CLK_NUM) + return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret, + "cannot get clocks\n"); + for (i = 0; i < RSPI_CLK_NUM; i++) { + if (!strcmp(clks[i].id, "tclk")) { + rspi->tclk = clks[i].clk; + break; + } + } + + if (!rspi->tclk) + return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n"); + + tclk_rate = clk_get_rate(rspi->tclk); + + rspi->resets[0].id = "presetn"; + rspi->resets[1].id = "tresetn"; + ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM, + rspi->resets); + if (ret) + return dev_err_probe(dev, ret, "cannot get resets\n"); + + irq_rx = platform_get_irq_byname(pdev, "rx"); + if (irq_rx < 0) + return dev_err_probe(dev, irq_rx, "cannot get IRQ 'rx'\n"); + + ret = reset_control_bulk_deassert(RSPI_RESET_NUM, rspi->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + init_waitqueue_head(&rspi->wait); + + ret = devm_request_irq(dev, irq_rx, rzv2h_rx_irq_handler, 0, + dev_name(dev), rspi); + if (ret) { + dev_err(dev, "cannot request `rx` IRQ\n"); + goto quit_resets; + } + + controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST; + controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + controller->prepare_message = rzv2h_rspi_prepare_message; + controller->unprepare_message = rzv2h_rspi_unprepare_message; + controller->num_chipselect = 4; + controller->transfer_one = rzv2h_rspi_transfer_one; + controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, + RSPI_SPBR_SPR_MAX, + RSPI_SPCMD_BRDV_MAX); + controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, + RSPI_SPBR_SPR_MIN, + RSPI_SPCMD_BRDV_MIN); + + device_set_node(&controller->dev, dev_fwnode(dev)); + + ret = spi_register_controller(controller); + if (ret) { + dev_err(dev, "register controller failed\n"); + goto quit_resets; + } + + return 0; + +quit_resets: + reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); + + return ret; +} + +static void rzv2h_rspi_remove(struct platform_device *pdev) +{ + struct rzv2h_rspi_priv *rspi = platform_get_drvdata(pdev); + + spi_unregister_controller(rspi->controller); + + reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); +} + +static const struct of_device_id rzv2h_rspi_match[] = { + { .compatible = "renesas,r9a09g057-rspi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzv2h_rspi_match); + +static struct platform_driver rzv2h_rspi_drv = { + .probe = rzv2h_rspi_probe, + .remove = rzv2h_rspi_remove, + .driver = { + .name = "rzv2h_rspi", + .of_match_table = rzv2h_rspi_match, + }, +}; +module_platform_driver(rzv2h_rspi_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabrizio Castro "); +MODULE_DESCRIPTION("Renesas RZ/V2H(P) Serial Peripheral Interface Driver"); -- GitLab From 678bae2eaa812662929a83b3de399645e9de93ad Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Jul 2025 17:35:43 +0200 Subject: [PATCH 860/868] gpiolib: make legacy interfaces optional The traditional interfaces are only used on a small number of ancient boards. Make these optional now so they can be disabled by default. Signed-off-by: Arnd Bergmann Reviewed-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20250722153634.3683927-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 +++ drivers/gpio/Makefile | 2 +- include/linux/gpio.h | 10 ++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6e3c3f0e3dcfe..500d839f65ee8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -14,6 +14,9 @@ menuconfig GPIOLIB if GPIOLIB +config GPIOLIB_LEGACY + def_bool y + config GPIOLIB_FASTPATH_LIMIT int "Maximum number of GPIOs for fast path" range 32 512 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3e5bc90ba59e5..379f55e9ed1e6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,7 +5,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o -obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o +obj-$(CONFIG_GPIOLIB_LEGACY) += gpiolib-legacy.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o diff --git a/include/linux/gpio.h b/include/linux/gpio.h index ff99ed76fdc33..8f85ddb264295 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -13,6 +13,11 @@ #define __LINUX_GPIO_H #include +#ifdef CONFIG_GPIOLIB +#include +#endif + +#ifdef CONFIG_GPIOLIB_LEGACY struct device; @@ -22,9 +27,6 @@ struct device; #define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1)) #ifdef CONFIG_GPIOLIB - -#include - /* * "valid" GPIO numbers are nonnegative and may be passed to * setup routines like gpio_request(). Only some valid numbers @@ -170,5 +172,5 @@ static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, } #endif /* ! CONFIG_GPIOLIB */ - +#endif /* CONFIG_GPIOLIB_LEGACY */ #endif /* __LINUX_GPIO_H */ -- GitLab From 0bd042ae771d61ef7ccd5882f7aeca59a25f71d9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Jul 2025 12:48:32 +0100 Subject: [PATCH 861/868] regulator: mt6370: Fix spelling mistake in mt6370_regualtor_register The function name mt6370_regualtor_register contains a spelling mistake, fix it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250724114832.146718-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mt6370-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index 27cb32b726e00..c2cea904b0ca9 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -320,7 +320,7 @@ static int mt6370_regulator_irq_register(struct mt6370_priv *priv) return 0; } -static int mt6370_regualtor_register(struct mt6370_priv *priv) +static int mt6370_regulator_register(struct mt6370_priv *priv) { struct regulator_dev *rdev; struct regulator_config cfg = {}; @@ -363,7 +363,7 @@ static int mt6370_regulator_probe(struct platform_device *pdev) return -ENODEV; } - ret = mt6370_regualtor_register(priv); + ret = mt6370_regulator_register(priv); if (ret) return ret; -- GitLab From 68b9272ca7ac948b71aba482ef8244dee8032f46 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Jul 2025 11:41:48 +0100 Subject: [PATCH 862/868] pwm: raspberrypi-poe: Fix spelling mistake "Firwmware" -> "Firmware" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a spelling mistake in the PWM_RASPBERRYPI_POE Kconfig, fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20250724104148.139559-1-colin.i.king@gmail.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 64f1c86340fdc..f00ce973dddf6 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -536,7 +536,7 @@ config PWM_PXA will be called pwm-pxa. config PWM_RASPBERRYPI_POE - tristate "Raspberry Pi Firwmware PoE Hat PWM support" + tristate "Raspberry Pi Firmware PoE Hat PWM support" # Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only # happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE. depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE) -- GitLab From 4740e1e2f320061c2f0dbadc0dd3dfb58df986d5 Mon Sep 17 00:00:00 2001 From: Harald Mommer Date: Thu, 24 Jul 2025 16:36:53 +0200 Subject: [PATCH 863/868] gpio: virtio: Fix config space reading. Quote from the virtio specification chapter 4.2.2.2: "For the device-specific configuration space, the driver MUST use 8 bit wide accesses for 8 bit wide fields, 16 bit wide and aligned accesses for 16 bit wide fields and 32 bit wide and aligned accesses for 32 and 64 bit wide fields." Signed-off-by: Harald Mommer Cc: stable@vger.kernel.org Fixes: 3a29355a22c0 ("gpio: Add virtio-gpio driver") Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20250724143718.5442-2-harald.mommer@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-virtio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-virtio.c b/drivers/gpio/gpio-virtio.c index 92b456475d895..07552611da988 100644 --- a/drivers/gpio/gpio-virtio.c +++ b/drivers/gpio/gpio-virtio.c @@ -527,7 +527,6 @@ static const char **virtio_gpio_get_names(struct virtio_gpio *vgpio, static int virtio_gpio_probe(struct virtio_device *vdev) { - struct virtio_gpio_config config; struct device *dev = &vdev->dev; struct virtio_gpio *vgpio; struct irq_chip *gpio_irq_chip; @@ -540,9 +539,11 @@ static int virtio_gpio_probe(struct virtio_device *vdev) return -ENOMEM; /* Read configuration */ - virtio_cread_bytes(vdev, 0, &config, sizeof(config)); - gpio_names_size = le32_to_cpu(config.gpio_names_size); - ngpio = le16_to_cpu(config.ngpio); + gpio_names_size = + virtio_cread32(vdev, offsetof(struct virtio_gpio_config, + gpio_names_size)); + ngpio = virtio_cread16(vdev, offsetof(struct virtio_gpio_config, + ngpio)); if (!ngpio) { dev_err(dev, "Number of GPIOs can't be zero\n"); return -EINVAL; -- GitLab From ffc72771ff6ec9f5b431a86c4b00d8ef0fea958b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 25 Jul 2025 13:03:27 +0200 Subject: [PATCH 864/868] regmap: Annotate that MMIO implies fast IO Document that using the MMIO helpers will automatically enable the 'fast_io' parameter. This makes the used locking scheme more transparent and avoids superfluous setting of this parameter in drivers. Signed-off-by: Wolfram Sang Link: https://patch.msgid.link/20250725110337.4303-2-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- include/linux/regmap.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 02b83f5499b8a..4e1ac1fbcec43 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -913,7 +913,7 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. + * a struct regmap. Implies 'fast_io'. */ #define regmap_init_mmio_clk(dev, clk_id, regs, config) \ __regmap_lockdep_wrapper(__regmap_init_mmio_clk, #config, \ @@ -927,7 +927,7 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to - * a struct regmap. + * a struct regmap. Implies 'fast_io'. */ #define regmap_init_mmio(dev, regs, config) \ regmap_init_mmio_clk(dev, NULL, regs, config) @@ -1138,7 +1138,7 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); * * The return value will be an ERR_PTR() on error or a valid pointer * to a struct regmap. The regmap will be automatically freed by the - * device management code. + * device management code. Implies 'fast_io'. */ #define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \ @@ -1153,7 +1153,7 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); * * The return value will be an ERR_PTR() on error or a valid pointer * to a struct regmap. The regmap will be automatically freed by the - * device management code. + * device management code. Implies 'fast_io'. */ #define devm_regmap_init_mmio(dev, regs, config) \ devm_regmap_init_mmio_clk(dev, NULL, regs, config) -- GitLab From 87aa3c8d8c4aa2e2567fe04126d14eb9fde815e5 Mon Sep 17 00:00:00 2001 From: Jakub Czapiga Date: Fri, 25 Jul 2025 12:25:42 +0000 Subject: [PATCH 865/868] spi: intel: Allow writeable MTD partition with module param The MTD device is blocked from writing to the SPI-NOR chip if any region of it is write-protected, even if "writeable=1" module parameter is set. Add ability to bypass this behaviour by introducing new module parameter "ignore_protestion_status" which allows to rely on the write protection mechanism of SPI-NOR chip itself, which most modern chips (since the 1990'+) have already implemented. Any erase/write operations performed on the write-protected section will be rejected by the chip. Signed-off-by: Jakub Czapiga Link: https://patch.msgid.link/20250725122542.2633334-1-czapiga@google.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 5d5a546c62ea7..13bbb2133507d 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -189,6 +189,11 @@ struct intel_spi_mem_op { static bool writeable; module_param(writeable, bool, 0); MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)"); +static bool ignore_protection_status; +module_param(ignore_protection_status, bool, 0); +MODULE_PARM_DESC( + ignore_protection_status, + "Do not block SPI flash chip write access even if it is write-protected (default=0)"); static void intel_spi_dump_regs(struct intel_spi *ispi) { @@ -1248,13 +1253,15 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, continue; /* - * If any of the regions have protection bits set, make the - * whole partition read-only to be on the safe side. + * If any of the regions have protection bits set and + * the ignore protection status parameter is not set, + * make the whole partition read-only to be on the safe side. * * Also if the user did not ask the chip to be writeable * mask the bit too. */ - if (!writeable || intel_spi_is_protected(ispi, base, limit)) { + if (!writeable || (!ignore_protection_status && + intel_spi_is_protected(ispi, base, limit))) { part->mask_flags |= MTD_WRITEABLE; ispi->protected = true; } -- GitLab From 2d442a0c781403702de27ccfbc4bb233721585f5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Jul 2025 18:17:01 +0100 Subject: [PATCH 866/868] spi: SPISG: Fix less than zero comparison on a u32 variable The check for ns < 0 is always false because variable ns is a u32 which is not a signed type. Fix this by making ns a s32 type. Fixes: cef9991e04ae ("spi: Add Amlogic SPISG driver") Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250725171701.839927-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spisg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index 48a5325b4db7b..2ab8bdf2a6761 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -163,7 +163,7 @@ struct spisg_device { static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay) { - u32 ns; + s32 ns; if (!delay) return 0; -- GitLab From a86240a37d43fc22b4e4953242fca8d90df2c555 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 26 Jul 2025 23:10:43 +0200 Subject: [PATCH 867/868] gpiolib: enable CONFIG_GPIOLIB_LEGACY even for !GPIOLIB A few drivers that use the legacy GPIOLIB interfaces can be enabled even when GPIOLIB is disabled entirely. With my previous patch this now causes build failures like: drivers/nfc/s3fwrn5/uart.c: In function 's3fwrn82_uart_parse_dt': drivers/nfc/s3fwrn5/uart.c:100:14: error: implicit declaration of function 'gpio_is_valid'; did you mean 'uuid_is_valid'? [-Werror=implicit-function-declaration] These did not show up in my randconfig tests because randconfig almost always has GPIOLIB selected by some other driver, and I did most of the testing with follow-up patches that address the failures properly. Move the symbol outside of the 'if CONFIG_GPIOLIB' block for the moment to avoid the build failures. It can be moved back and turned off by default once all the driver specific changes are merged. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202507261934.yIHeUuEQ-lkp@intel.com/ Fixes: 678bae2eaa81 ("gpiolib: make legacy interfaces optional") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250726211053.2226857-1-arnd@kernel.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 500d839f65ee8..e43abb322fa6e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -12,11 +12,11 @@ menuconfig GPIOLIB If unsure, say N. -if GPIOLIB - config GPIOLIB_LEGACY def_bool y +if GPIOLIB + config GPIOLIB_FASTPATH_LIMIT int "Maximum number of GPIOs for fast path" range 32 512 -- GitLab From 6b94bf976f9f9e6d4a6bf3218968a506c049702e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sun, 27 Jul 2025 10:24:42 +0200 Subject: [PATCH 868/868] MIPS: alchemy: gpio: use new GPIO line value setter callbacks for the remaining chips Previous commit missed two other places that need converting, it only came out in tests on autobuilders now. Convert the rest of the driver. Fixes: 68bdc4dc1130 ("MIPS: alchemy: gpio: use new line value setter callbacks") Acked-by: Thomas Bogendoerfer Link: https://lore.kernel.org/r/20250727082442.13182-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- arch/mips/alchemy/common/gpiolib.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c index 411f70ceb762a..194034eba75fd 100644 --- a/arch/mips/alchemy/common/gpiolib.c +++ b/arch/mips/alchemy/common/gpiolib.c @@ -40,9 +40,11 @@ static int gpio2_get(struct gpio_chip *chip, unsigned offset) return !!alchemy_gpio2_get_value(offset + ALCHEMY_GPIO2_BASE); } -static void gpio2_set(struct gpio_chip *chip, unsigned offset, int value) +static int gpio2_set(struct gpio_chip *chip, unsigned offset, int value) { alchemy_gpio2_set_value(offset + ALCHEMY_GPIO2_BASE, value); + + return 0; } static int gpio2_direction_input(struct gpio_chip *chip, unsigned offset) @@ -68,10 +70,12 @@ static int gpio1_get(struct gpio_chip *chip, unsigned offset) return !!alchemy_gpio1_get_value(offset + ALCHEMY_GPIO1_BASE); } -static void gpio1_set(struct gpio_chip *chip, +static int gpio1_set(struct gpio_chip *chip, unsigned offset, int value) { alchemy_gpio1_set_value(offset + ALCHEMY_GPIO1_BASE, value); + + return 0; } static int gpio1_direction_input(struct gpio_chip *chip, unsigned offset) @@ -97,7 +101,7 @@ struct gpio_chip alchemy_gpio_chip[] = { .direction_input = gpio1_direction_input, .direction_output = gpio1_direction_output, .get = gpio1_get, - .set = gpio1_set, + .set_rv = gpio1_set, .to_irq = gpio1_to_irq, .base = ALCHEMY_GPIO1_BASE, .ngpio = ALCHEMY_GPIO1_NUM, @@ -107,7 +111,7 @@ struct gpio_chip alchemy_gpio_chip[] = { .direction_input = gpio2_direction_input, .direction_output = gpio2_direction_output, .get = gpio2_get, - .set = gpio2_set, + .set_rv = gpio2_set, .to_irq = gpio2_to_irq, .base = ALCHEMY_GPIO2_BASE, .ngpio = ALCHEMY_GPIO2_NUM, -- GitLab