From 98e9645a35993f8cfe99e36c9ba3e6a8c1783d78 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 11 Jan 2024 00:50:27 +0000 Subject: ASoC: ti: use devm_snd_soc_register_card() Let's use devm_snd_soc_register_card() instead of snd_soc_register_card() and ignore snd_soc_unregister_card() Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87zfxcwv58.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-hdmi.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index 29bff9e6337b..4513b527ab97 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -379,7 +379,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->num_links = 1; card->dev = dev; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(dev, card); if (ret) { dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); return ret; @@ -393,19 +393,11 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) return 0; } -static void omap_hdmi_audio_remove(struct platform_device *pdev) -{ - struct hdmi_audio_data *ad = platform_get_drvdata(pdev); - - snd_soc_unregister_card(ad->card); -} - static struct platform_driver hdmi_audio_driver = { .driver = { .name = DRV_NAME, }, .probe = omap_hdmi_audio_probe, - .remove_new = omap_hdmi_audio_remove, }; module_platform_driver(hdmi_audio_driver); -- cgit v1.2.3 From a4005007161c9df8fa4f44a776c624f68ec34a69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 11 Jan 2024 00:50:34 +0000 Subject: ASoC: fsl: use devm_snd_soc_register_card() Let's use devm_snd_soc_register_card() instead of snd_soc_register_card() and ignore snd_soc_unregister_card() Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87y1cwwv51.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/fsl/eukrea-tlv320.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 63f1f05da947..6be074ea0b3f 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -196,7 +196,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev) } } - ret = snd_soc_register_card(&eukrea_tlv320); + ret = devm_snd_soc_register_card(&pdev->dev, &eukrea_tlv320); err: if (ret) dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -205,11 +205,6 @@ err: return ret; } -static void eukrea_tlv320_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&eukrea_tlv320); -} - static const struct of_device_id imx_tlv320_dt_ids[] = { { .compatible = "eukrea,asoc-tlv320"}, { /* sentinel */ } @@ -222,7 +217,6 @@ static struct platform_driver eukrea_tlv320_driver = { .of_match_table = imx_tlv320_dt_ids, }, .probe = eukrea_tlv320_probe, - .remove_new = eukrea_tlv320_remove, }; module_platform_driver(eukrea_tlv320_driver); -- cgit v1.2.3 From 00352af2504a90381ec733237c3ef444032d5f1f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 11 Jan 2024 00:50:44 +0000 Subject: ASoC: atmel: use devm_snd_soc_register_card() Let's use devm_snd_soc_register_card() instead of snd_soc_register_card() and ignore snd_soc_unregister_card() Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87wmsgwv4r.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/atmel/mikroe-proto.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index 18a8760443ae..8341a6e06493 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -140,7 +140,7 @@ static int snd_proto_probe(struct platform_device *pdev) dai->dai_fmt = dai_fmt; - ret = snd_soc_register_card(&snd_proto); + ret = devm_snd_soc_register_card(&pdev->dev, &snd_proto); if (ret) dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); @@ -155,11 +155,6 @@ put_codec_node: return ret; } -static void snd_proto_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&snd_proto); -} - static const struct of_device_id snd_proto_of_match[] = { { .compatible = "mikroe,mikroe-proto", }, {}, @@ -172,7 +167,6 @@ static struct platform_driver snd_proto_driver = { .of_match_table = snd_proto_of_match, }, .probe = snd_proto_probe, - .remove_new = snd_proto_remove, }; module_platform_driver(snd_proto_driver); -- cgit v1.2.3 From 2f2d78e2c29347a96268f6f34092538b307ed056 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Fri, 12 Jan 2024 14:43:30 +0900 Subject: ASoC: fsl_sai: Add support for i.MX95 platform Add compatible string and specific soc data to support SAI on i.MX95 platform. Signed-off-by: Chancel Liu Link: https://msgid.link/r/20240112054331.3244104-3-chancel.liu@nxp.com Reviewed-by: Daniel Baluta Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 546bd4e333b5..0e2c31439670 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1639,6 +1639,18 @@ static const struct fsl_sai_soc_data fsl_sai_imx93_data = { .max_burst = {8, 8}, }; +static const struct fsl_sai_soc_data fsl_sai_imx95_data = { + .use_imx_pcm = true, + .use_edma = true, + .fifo_depth = 128, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 8, + .flags = 0, + .max_register = FSL_SAI_MCTL, + .max_burst = {8, 8}, +}; + static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data }, { .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data }, @@ -1651,6 +1663,7 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data }, { .compatible = "fsl,imx8mn-sai", .data = &fsl_sai_imx8mn_data }, { .compatible = "fsl,imx93-sai", .data = &fsl_sai_imx93_data }, + { .compatible = "fsl,imx95-sai", .data = &fsl_sai_imx95_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); -- cgit v1.2.3 From 0c105997eefd98603796c4e5890615527578eb04 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 19 Dec 2023 13:45:36 +0100 Subject: ASoC: codec: wcd-mbhc-v2: add support when connected behind an USB-C audio mux When the WCD codec is connected behind an USB-C audio mux, plug/unplug events, clock control, pull-up and threshold are different. Add a typec_analog_mux config enabling those changes and add two callbacks to trigger plug/unplug events from USB-C events. Signed-off-by: Neil Armstrong Link: https://msgid.link/r/20231219-topic-sm8650-upstream-wcd939x-codec-v4-3-1c3bbff2d7ab@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd-mbhc-v2.c | 95 +++++++++++++++++++++++++++++++++++------- sound/soc/codecs/wcd-mbhc-v2.h | 3 ++ 2 files changed, 83 insertions(+), 15 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 5da1934527f3..0e6218ed0e5e 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -16,6 +16,7 @@ #define HS_DETECT_PLUG_TIME_MS (3 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 +#define GND_MIC_USBC_SWAP_THRESHOLD 2 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HPHL_CROSS_CONN_THRESHOLD 100 #define HS_VREF_MIN_VAL 1400 @@ -52,12 +53,15 @@ struct wcd_mbhc { struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; + /* Work to handle plug report */ + struct work_struct mbhc_plug_detect_work; /* Work to correct accessory type */ struct work_struct correct_plug_swch; struct mutex lock; int buttons_pressed; u32 hph_status; /* track headhpone status */ u8 current_plug; + unsigned int swap_thr; bool is_btn_press; bool in_swch_irq_handler; bool hs_detect_work_stop; @@ -506,14 +510,13 @@ static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) } } -static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +static void mbhc_plug_detect_fn(struct work_struct *work) { - struct snd_soc_component *component; + struct wcd_mbhc *mbhc = container_of(work, struct wcd_mbhc, mbhc_plug_detect_work); + struct snd_soc_component *component = mbhc->component; enum snd_jack_types jack_type; - struct wcd_mbhc *mbhc = data; bool detection_type; - component = mbhc->component; mutex_lock(&mbhc->lock); mbhc->in_swch_irq_handler = true; @@ -576,9 +579,51 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) exit: mbhc->in_swch_irq_handler = false; mutex_unlock(&mbhc->lock); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + if (!mbhc->cfg->typec_analog_mux) + schedule_work(&mbhc->mbhc_plug_detect_work); + return IRQ_HANDLED; } +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc) +{ + + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, false); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, 0); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_unplug); + +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc) +{ + if (!mbhc || !mbhc->cfg->typec_analog_mux) + return -EINVAL; + + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(mbhc->component, true); + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + + schedule_work(&mbhc->mbhc_plug_detect_work); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_typec_report_plug); + static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) { int mask = 0; @@ -725,14 +770,23 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); - /* enable HS detection */ + if (mbhc->cfg->typec_analog_mux) + mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; + else + mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD; + + /* setup HS detection */ if (mbhc->mbhc_cb->hph_pull_up_control_v2) mbhc->mbhc_cb->hph_pull_up_control_v2(component, - HS_PULLUP_I_DEFAULT); + mbhc->cfg->typec_analog_mux ? + HS_PULLUP_I_OFF : HS_PULLUP_I_DEFAULT); else if (mbhc->mbhc_cb->hph_pull_up_control) - mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT); + mbhc->mbhc_cb->hph_pull_up_control(component, + mbhc->cfg->typec_analog_mux ? + I_OFF : I_DEFAULT); else - wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + mbhc->cfg->typec_analog_mux ? 0 : 3); wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh); wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh); @@ -741,10 +795,18 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true); wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); - wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + /* Plug detect is triggered manually if analog goes through USBCC */ + if (mbhc->cfg->typec_analog_mux) + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 0); + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); - /* Insertion debounce set to 96ms */ - wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); + if (mbhc->cfg->typec_analog_mux) + /* Insertion debounce set to 48ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 4); + else + /* Insertion debounce set to 96ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); /* Button Debounce set to 16ms */ wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2); @@ -753,7 +815,8 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) mbhc->mbhc_cb->mbhc_bias(component, true); /* enable MBHC clock */ if (mbhc->mbhc_cb->clk_setup) - mbhc->mbhc_cb->clk_setup(component, true); + mbhc->mbhc_cb->clk_setup(component, + mbhc->cfg->typec_analog_mux ? false : true); /* program HS_VREF value */ wcd_program_hs_vref(mbhc); @@ -1115,7 +1178,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) do { cross_conn = wcd_check_cross_conn(mbhc); try++; - } while (try < GND_MIC_SWAP_THRESHOLD); + } while (try < mbhc->swap_thr); if (cross_conn > 0) { plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1183,7 +1246,7 @@ correct_plug_type: cross_conn = wcd_check_cross_conn(mbhc); if (cross_conn > 0) { /* cross-connection */ pt_gnd_mic_swap_cnt++; - if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD) + if (pt_gnd_mic_swap_cnt < mbhc->swap_thr) continue; else plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -1194,7 +1257,7 @@ correct_plug_type: } else /* Error if (cross_conn < 0) */ continue; - if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) { + if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) { /* US_EU gpio present, flip switch */ if (mbhc->cfg->swap_gnd_mic) { if (mbhc->cfg->swap_gnd_mic(component, true)) @@ -1473,6 +1536,7 @@ struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, mutex_init(&mbhc->lock); INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); + INIT_WORK(&mbhc->mbhc_plug_detect_work, mbhc_plug_detect_fn); ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL, wcd_mbhc_mech_plug_detect_irq, @@ -1562,6 +1626,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) mutex_lock(&mbhc->lock); wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + cancel_work_sync(&mbhc->mbhc_plug_detect_work); mutex_unlock(&mbhc->lock); kfree(mbhc); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 006118f3e81f..df68e99c81a3 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -193,6 +193,7 @@ struct wcd_mbhc_config { int v_hs_max; int num_btn; bool mono_stero_detection; + bool typec_analog_mux; bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active); bool hs_ext_micbias; bool gnd_det_en; @@ -273,6 +274,8 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, void wcd_mbhc_stop(struct wcd_mbhc *mbhc); void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type); int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_plug(struct wcd_mbhc *mbhc); +int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, -- cgit v1.2.3 From be2af391cea018eaea61f929eaef9394c78faaf2 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 19 Dec 2023 13:45:37 +0100 Subject: ASoC: codecs: Add WCD939x Soundwire devices driver Add Soundwire Slave driver for the WCD9390/WCD9395 Audio Codec. The WCD9390/WCD9395 Soundwire devices will be used by the main WCD9390/WCD9395 Audio Codec driver to access registers and configure Soundwire RX and TX ports. Signed-off-by: Neil Armstrong Link: https://msgid.link/r/20231219-topic-sm8650-upstream-wcd939x-codec-v4-4-1c3bbff2d7ab@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 1 + sound/soc/codecs/wcd939x-sdw.c | 1551 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/wcd939x.h | 989 +++++++++++++++++++++++++ 4 files changed, 2551 insertions(+) create mode 100644 sound/soc/codecs/wcd939x-sdw.c create mode 100644 sound/soc/codecs/wcd939x.h (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 59f9742e9ff4..78552a497eaa 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -276,6 +276,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WCD9335 imply SND_SOC_WCD934X imply SND_SOC_WCD938X_SDW + imply SND_SOC_WCD939X_SDW imply SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO @@ -2059,6 +2060,15 @@ config SND_SOC_WCD938X_SDW The WCD9380/9385 is a audio codec IC Integrated in Qualcomm SoCs like SM8250. +config SND_SOC_WCD939X_SDW + tristate "WCD9390/WCD9395 Codec - SDW" + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9390/9395 is a audio codec IC Integrated in + Qualcomm SoCs like SM8650. + config SND_SOC_WL1273 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f53baa2b9565..46f78d539278 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -313,6 +313,7 @@ snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd938x-objs := wcd938x.o snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o +snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c new file mode 100644 index 000000000000..8acb5651c5bc --- /dev/null +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -0,0 +1,1551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd939x.h" + +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) + +static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_L, WCD939X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_COMP_R, WCD939X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_LO, WCD939X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_L, WCD939X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DSD_R, WCD939X_DSD_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_HIFI_PCM_L, WCD939X_HIFI_PCM_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), +}; + +static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_ADC4, WCD939X_ADC_1_4_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC0, WCD939X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC1, WCD939X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_MBHC, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC2, WCD939X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC3, WCD939X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD939X_DMIC4, WCD939X_DMIC_3_7_PORT, BIT(0)), + WCD_SDW_CH(WCD939X_DMIC5, WCD939X_DMIC_3_7_PORT, BIT(1)), + WCD_SDW_CH(WCD939X_DMIC6, WCD939X_DMIC_3_7_PORT, BIT(2)), + WCD_SDW_CH(WCD939X_DMIC7, WCD939X_DMIC_3_7_PORT, BIT(3)), +}; + +static struct sdw_dpn_prop wcd939x_rx_dpn_prop[WCD939X_MAX_RX_SWR_PORTS] = { + { + .num = WCD939X_HPH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_CLSH_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_COMP_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_LO_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DSD_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_HIFI_PCM_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + } +}; + +static struct sdw_dpn_prop wcd939x_tx_dpn_prop[WCD939X_MAX_TX_SWR_PORTS] = { + { + .num = WCD939X_ADC_1_4_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_ADC_DMIC_1_2_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_0_3_MBHC_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, + { + .num = WCD939X_DMIC_3_7_PORT, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_device_get); + +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return FIELD_GET(SDW_SCP_STAT_CURR_BANK, + sdw_read(sdev, SDW_SCP_CTRL)); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_current_bank); + +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD939X_MAX_SWR_PORTS; i++) { + ch_mask = wcd->port_config[i].ch_mask; + + if (!ch_mask) + continue; + + for_each_set_bit(j, &ch_mask, 4) + wcd->sconfig.ch_count++; + + port_config[wcd->active_ports] = wcd->port_config[i]; + wcd->active_ports++; + } + + wcd->sconfig.bps = 1; + wcd->sconfig.frame_rate = params_rate(params); + if (wcd->is_tx) + wcd->sconfig.direction = SDW_DATA_DIR_TX; + else + wcd->sconfig.direction = SDW_DATA_DIR_RX; + + wcd->sconfig.type = SDW_STREAM_PCM; + + return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, &port_config[0], + wcd->active_ports, wcd->sruntime); +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_hw_params); + +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_free); + +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, void *stream, + int direction) +{ + wcd->sruntime = stream; + + return 0; +} +EXPORT_SYMBOL_GPL(wcd939x_sdw_set_sdw_stream); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + if (wcd->regmap) + return wcd->regmap; + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(wcd939x_swr_get_regmap); + +static int wcd9390_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + + if (wcd->regmap && status == SDW_SLAVE_ATTACHED) { + /* Write out any cached changes that happened between probe and attach */ + regcache_cache_only(wcd->regmap, false); + return regcache_sync(wcd->regmap); + } + + return 0; +} + +static int wcd9390_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +/* + * Handle Soundwire out-of-band interrupt event by triggering + * the first irq of the slave_irq irq domain, which then will + * be handled by the regmap_irq threaded irq. + * Looping is to ensure no interrupts were missed in the process. + */ +static int wcd9390_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + struct irq_domain *slave_irq = wcd->slave_irq; + u32 sts1, sts2, sts3; + + do { + handle_nested_irq(irq_find_mapping(slave_irq, 0)); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD939X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd939x_defaults[] = { + /* Default values except for Read-Only & Volatile registers */ + { WCD939X_ANA_PAGE, 0x00 }, + { WCD939X_ANA_BIAS, 0x00 }, + { WCD939X_ANA_RX_SUPPLIES, 0x00 }, + { WCD939X_ANA_HPH, 0x0c }, + { WCD939X_ANA_EAR, 0x00 }, + { WCD939X_ANA_EAR_COMPANDER_CTL, 0x02 }, + { WCD939X_ANA_TX_CH1, 0x20 }, + { WCD939X_ANA_TX_CH2, 0x00 }, + { WCD939X_ANA_TX_CH3, 0x20 }, + { WCD939X_ANA_TX_CH4, 0x00 }, + { WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MICB3_DSP_EN_LOGIC, 0x00 }, + { WCD939X_ANA_MBHC_MECH, 0x39 }, + { WCD939X_ANA_MBHC_ELECT, 0x08 }, + { WCD939X_ANA_MBHC_ZDET, 0x00 }, + { WCD939X_ANA_MBHC_BTN0, 0x00 }, + { WCD939X_ANA_MBHC_BTN1, 0x10 }, + { WCD939X_ANA_MBHC_BTN2, 0x20 }, + { WCD939X_ANA_MBHC_BTN3, 0x30 }, + { WCD939X_ANA_MBHC_BTN4, 0x40 }, + { WCD939X_ANA_MBHC_BTN5, 0x50 }, + { WCD939X_ANA_MBHC_BTN6, 0x60 }, + { WCD939X_ANA_MBHC_BTN7, 0x70 }, + { WCD939X_ANA_MICB1, 0x10 }, + { WCD939X_ANA_MICB2, 0x10 }, + { WCD939X_ANA_MICB2_RAMP, 0x00 }, + { WCD939X_ANA_MICB3, 0x00 }, + { WCD939X_ANA_MICB4, 0x00 }, + { WCD939X_BIAS_CTL, 0x2a }, + { WCD939X_BIAS_VBG_FINE_ADJ, 0x55 }, + { WCD939X_LDOL_VDDCX_ADJUST, 0x01 }, + { WCD939X_LDOL_DISABLE_LDOL, 0x00 }, + { WCD939X_MBHC_CTL_CLK, 0x00 }, + { WCD939X_MBHC_CTL_ANA, 0x00 }, + { WCD939X_MBHC_ZDET_VNEG_CTL, 0x00 }, + { WCD939X_MBHC_ZDET_BIAS_CTL, 0x46 }, + { WCD939X_MBHC_CTL_BCS, 0x00 }, + { WCD939X_MBHC_TEST_CTL, 0x00 }, + { WCD939X_LDOH_MODE, 0x2b }, + { WCD939X_LDOH_BIAS, 0x68 }, + { WCD939X_LDOH_STB_LOADS, 0x00 }, + { WCD939X_LDOH_SLOWRAMP, 0x50 }, + { WCD939X_MICB1_TEST_CTL_1, 0x1a }, + { WCD939X_MICB1_TEST_CTL_2, 0x00 }, + { WCD939X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD939X_MICB2_TEST_CTL_1, 0x1a }, + { WCD939X_MICB2_TEST_CTL_2, 0x00 }, + { WCD939X_MICB2_TEST_CTL_3, 0x24 }, + { WCD939X_MICB3_TEST_CTL_1, 0x9a }, + { WCD939X_MICB3_TEST_CTL_2, 0x80 }, + { WCD939X_MICB3_TEST_CTL_3, 0x24 }, + { WCD939X_MICB4_TEST_CTL_1, 0x1a }, + { WCD939X_MICB4_TEST_CTL_2, 0x80 }, + { WCD939X_MICB4_TEST_CTL_3, 0x24 }, + { WCD939X_TX_COM_ADC_VCM, 0x39 }, + { WCD939X_TX_COM_BIAS_ATEST, 0xe0 }, + { WCD939X_TX_COM_SPARE1, 0x00 }, + { WCD939X_TX_COM_SPARE2, 0x00 }, + { WCD939X_TX_COM_TXFE_DIV_CTL, 0x22 }, + { WCD939X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD939X_TX_COM_SPARE3, 0x00 }, + { WCD939X_TX_COM_SPARE4, 0x00 }, + { WCD939X_TX_1_2_TEST_EN, 0xcc }, + { WCD939X_TX_1_2_ADC_IB, 0xe9 }, + { WCD939X_TX_1_2_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_1_2_TEST_CTL, 0x38 }, + { WCD939X_TX_1_2_TEST_BLK_EN1, 0xff }, + { WCD939X_TX_1_2_TXFE1_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_EN, 0xcc }, + { WCD939X_TX_3_4_ADC_IB, 0xe9 }, + { WCD939X_TX_3_4_ATEST_REFCTL, 0x0b }, + { WCD939X_TX_3_4_TEST_CTL, 0x38 }, + { WCD939X_TX_3_4_TEST_BLK_EN3, 0xff }, + { WCD939X_TX_3_4_TXFE3_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN2, 0xfb }, + { WCD939X_TX_3_4_TXFE2_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE1, 0x00 }, + { WCD939X_TX_3_4_TEST_BLK_EN4, 0xfb }, + { WCD939X_TX_3_4_TXFE4_CLKDIV, 0x00 }, + { WCD939X_TX_3_4_SPARE2, 0x00 }, + { WCD939X_CLASSH_MODE_1, 0x40 }, + { WCD939X_CLASSH_MODE_2, 0x3a }, + { WCD939X_CLASSH_MODE_3, 0xf0 }, + { WCD939X_CLASSH_CTRL_VCL_1, 0x7c }, + { WCD939X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD939X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD939X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD939X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD939X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD939X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD939X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD939X_CLASSH_SPARE, 0x80 }, + { WCD939X_FLYBACK_EN, 0x4e }, + { WCD939X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD939X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD939X_FLYBACK_VNEG_CTRL_3, 0x14 }, + { WCD939X_FLYBACK_VNEG_CTRL_4, 0xdb }, + { WCD939X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD939X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD939X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD939X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD939X_FLYBACK_VNEG_CTRL_9, 0x66 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_2, 0xf8 }, + { WCD939X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD939X_FLYBACK_CTRL_1, 0x65 }, + { WCD939X_FLYBACK_TEST_CTL, 0x02 }, + { WCD939X_RX_AUX_SW_CTL, 0x00 }, + { WCD939X_RX_PA_AUX_IN_CONN, 0x01 }, + { WCD939X_RX_TIMER_DIV, 0x32 }, + { WCD939X_RX_OCP_CTL, 0x1f }, + { WCD939X_RX_OCP_COUNT, 0x77 }, + { WCD939X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD939X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD939X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD939X_RX_BIAS_HPH_PA, 0xaa }, + { WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2, 0xca }, + { WCD939X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD939X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD939X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD939X_RX_BIAS_AUX_DAC, 0xa0 }, + { WCD939X_RX_BIAS_AUX_AMP, 0xaa }, + { WCD939X_RX_BIAS_VNEGDAC_BLEEDER, 0x50 }, + { WCD939X_RX_BIAS_MISC, 0x00 }, + { WCD939X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD939X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD939X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD939X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD939X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD939X_HPH_CNP_EN, 0x80 }, + { WCD939X_HPH_CNP_WG_CTL, 0x9a }, + { WCD939X_HPH_CNP_WG_TIME, 0x14 }, + { WCD939X_HPH_OCP_CTL, 0x28 }, + { WCD939X_HPH_AUTO_CHOP, 0x56 }, + { WCD939X_HPH_CHOP_CTL, 0x83 }, + { WCD939X_HPH_PA_CTL1, 0x46 }, + { WCD939X_HPH_PA_CTL2, 0x50 }, + { WCD939X_HPH_L_EN, 0x80 }, + { WCD939X_HPH_L_TEST, 0xe0 }, + { WCD939X_HPH_L_ATEST, 0x50 }, + { WCD939X_HPH_R_EN, 0x80 }, + { WCD939X_HPH_R_TEST, 0xe0 }, + { WCD939X_HPH_R_ATEST, 0x50 }, + { WCD939X_HPH_RDAC_CLK_CTL1, 0x80 }, + { WCD939X_HPH_RDAC_CLK_CTL2, 0x0b }, + { WCD939X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_UHQA_CTL, 0x00 }, + { WCD939X_HPH_REFBUFF_LP_CTL, 0x8e }, + { WCD939X_HPH_L_DAC_CTL, 0x20 }, + { WCD939X_HPH_R_DAC_CTL, 0x20 }, + { WCD939X_HPH_SURGE_COMP_SEL, 0x55 }, + { WCD939X_HPH_SURGE_EN, 0x19 }, + { WCD939X_HPH_SURGE_MISC1, 0xa0 }, + { WCD939X_EAR_EN, 0x22 }, + { WCD939X_EAR_PA_CON, 0x44 }, + { WCD939X_EAR_SP_CON, 0xdb }, + { WCD939X_EAR_DAC_CON, 0x80 }, + { WCD939X_EAR_CNP_FSM_CON, 0xb2 }, + { WCD939X_EAR_TEST_CTL, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_2, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_3, 0x00 }, + { WCD939X_FLYBACK_NEW_CTRL_4, 0x44 }, + { WCD939X_ANA_NEW_PAGE, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD939X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD939X_SLEEP_CTL, 0x18 }, + { WCD939X_SLEEP_WATCHDOG_CTL, 0x00 }, + { WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD939X_MBHC_NEW_CTL_1, 0x02 }, + { WCD939X_MBHC_NEW_CTL_2, 0x05 }, + { WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD939X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD939X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD939X_TX_NEW_CH12_MUX, 0x11 }, + { WCD939X_TX_NEW_CH34_MUX, 0x23 }, + { WCD939X_DIE_CRACK_DET_EN, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_VREF_CTL, 0x08 }, + { WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_MISC1, 0x32 }, + { WCD939X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD939X_HPH_NEW_INT_TIMER1, 0xfe }, + { WCD939X_HPH_NEW_INT_TIMER2, 0x02 }, + { WCD939X_HPH_NEW_INT_TIMER3, 0x4e }, + { WCD939X_HPH_NEW_INT_TIMER4, 0x54 }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC2, 0x0b }, + { WCD939X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xa0 }, + { WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xa0 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x64 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57 }, + { WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01 }, + { WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00 }, + { WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW, 0x47 }, + { WCD939X_EAR_INT_NEW_CHOPPER_CON, 0xa8 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON1, 0x42 }, + { WCD939X_EAR_INT_NEW_CNP_VCM_CON2, 0x22 }, + { WCD939X_EAR_INT_NEW_DYNAMIC_BIAS, 0x00 }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_1, 0x0a }, + { WCD939X_SLEEP_INT_WATCHDOG_CTL_2, 0x0a }, + { WCD939X_DIE_CRACK_INT_DET_INT1, 0x02 }, + { WCD939X_DIE_CRACK_INT_DET_INT2, 0x60 }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1, 0x7f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0, 0x3f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M, 0x1f }, + { WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M, 0x0f }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1, 0xd7 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0, 0xc8 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP, 0xc6 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1, 0x95 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0, 0x6a }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, 0x05 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0, 0xa5 }, + { WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, 0x13 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1, 0x88 }, + { WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP, 0x42 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L2, 0xff }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L1, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_L0, 0x64 }, + { WCD939X_TX_COM_NEW_INT_ADC_INT_ULP, 0x77 }, + { WCD939X_DIGITAL_PAGE, 0x00 }, + { WCD939X_DIGITAL_SWR_TX_CLK_RATE, 0x00 }, + { WCD939X_DIGITAL_CDC_RST_CTL, 0x03 }, + { WCD939X_DIGITAL_TOP_CLK_CFG, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_CLK_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_DIG_CLK_CTL, 0xf0 }, + { WCD939X_DIGITAL_SWR_RST_EN, 0x00 }, + { WCD939X_DIGITAL_CDC_PATH_MODE, 0x55 }, + { WCD939X_DIGITAL_CDC_RX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_RX0_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX1_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_RX2_CTL, 0xfc }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00 }, + { WCD939X_DIGITAL_CDC_COMP_CTL_0, 0x00 }, + { WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1e }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_0, 0xac }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1a }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_0, 0xbc }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A6_0, 0xc7 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_A7_0, 0xf8 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_0, 0x47 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_1, 0x43 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_2, 0xb1 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_C_3, 0x17 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R1, 0x4d }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R2, 0x29 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R3, 0x34 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R4, 0x59 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R5, 0x66 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R6, 0x87 }, + { WCD939X_DIGITAL_CDC_HPH_DSM_R7, 0x64 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A1_1, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_0, 0x96 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A2_1, 0x09 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_0, 0xab }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A3_1, 0x05 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_0, 0x1c }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A4_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_0, 0x17 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A5_1, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A6_0, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_A7_0, 0xe3 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_0, 0x69 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_1, 0x54 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_2, 0x02 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_C_3, 0x15 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R1, 0xa4 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R2, 0xb5 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R3, 0x86 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R4, 0x85 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R5, 0xaa }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R6, 0xe2 }, + { WCD939X_DIGITAL_CDC_EAR_DSM_R7, 0x62 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xa9 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3d }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2e }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1, 0xfc }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2, 0x01 }, + { WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_SWR_CLH, 0x00 }, + { WCD939X_DIGITAL_SWR_CLH_BYP, 0x00 }, + { WCD939X_DIGITAL_CDC_TX0_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX1_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX2_CTL, 0x68 }, + { WCD939X_DIGITAL_CDC_TX_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_REQ_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_RST, 0x00 }, + { WCD939X_DIGITAL_CDC_AMIC_CTL, 0x0f }, + { WCD939X_DIGITAL_CDC_DMIC_CTL, 0x04 }, + { WCD939X_DIGITAL_CDC_DMIC1_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC2_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC3_CTL, 0x01 }, + { WCD939X_DIGITAL_CDC_DMIC4_CTL, 0x01 }, + { WCD939X_DIGITAL_EFUSE_PRG_CTL, 0x00 }, + { WCD939X_DIGITAL_EFUSE_CTL, 0x2b }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11 }, + { WCD939X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11 }, + { WCD939X_DIGITAL_PDM_WD_CTL0, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL1, 0x00 }, + { WCD939X_DIGITAL_PDM_WD_CTL2, 0x00 }, + { WCD939X_DIGITAL_INTR_MODE, 0x00 }, + { WCD939X_DIGITAL_INTR_MASK_0, 0xff }, + { WCD939X_DIGITAL_INTR_MASK_1, 0xe7 }, + { WCD939X_DIGITAL_INTR_MASK_2, 0x0e }, + { WCD939X_DIGITAL_INTR_CLEAR_0, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_1, 0x00 }, + { WCD939X_DIGITAL_INTR_CLEAR_2, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_0, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_1, 0x00 }, + { WCD939X_DIGITAL_INTR_LEVEL_2, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_0, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_1, 0x00 }, + { WCD939X_DIGITAL_INTR_SET_2, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_0, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_1, 0x00 }, + { WCD939X_DIGITAL_INTR_TEST_2, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_EN, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_0_1, 0x00 }, + { WCD939X_DIGITAL_TX_MODE_DBG_2_3, 0x00 }, + { WCD939X_DIGITAL_LB_IN_SEL_CTL, 0x00 }, + { WCD939X_DIGITAL_LOOP_BACK_MODE, 0x00 }, + { WCD939X_DIGITAL_SWR_DAC_TEST, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_0, 0x40 }, + { WCD939X_DIGITAL_SWR_HM_TEST_RX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_1, 0x00 }, + { WCD939X_DIGITAL_SWR_HM_TEST_TX_2, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_SWR_0, 0x8f }, + { WCD939X_DIGITAL_PAD_CTL_SWR_1, 0x06 }, + { WCD939X_DIGITAL_I2C_CTL, 0x00 }, + { WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_0, 0x00 }, + { WCD939X_DIGITAL_EFUSE_TEST_CTL_1, 0x00 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_RX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX0, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX1, 0xf1 }, + { WCD939X_DIGITAL_PAD_CTL_PDM_TX2, 0xf1 }, + { WCD939X_DIGITAL_PAD_INP_DIS_0, 0x00 }, + { WCD939X_DIGITAL_PAD_INP_DIS_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_0, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_1, 0x00 }, + { WCD939X_DIGITAL_DRIVE_STRENGTH_2, 0x00 }, + { WCD939X_DIGITAL_RX_DATA_EDGE_CTL, 0x1f }, + { WCD939X_DIGITAL_TX_DATA_EDGE_CTL, 0x80 }, + { WCD939X_DIGITAL_GPIO_MODE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_OE, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_0, 0x00 }, + { WCD939X_DIGITAL_PIN_CTL_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_CTL, 0x00 }, + { WCD939X_DIGITAL_DIG_DEBUG_EN, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_ADD, 0x00 }, + { WCD939X_DIGITAL_ANA_CSR_DBG_CTL, 0x48 }, + { WCD939X_DIGITAL_SSP_DBG, 0x00 }, + { WCD939X_DIGITAL_SPARE_0, 0x00 }, + { WCD939X_DIGITAL_SPARE_1, 0x00 }, + { WCD939X_DIGITAL_SPARE_2, 0x00 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_0, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_1, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_2, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_3, 0x88 }, + { WCD939X_DIGITAL_TX_REQ_FB_CTL_4, 0x88 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA0, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA1, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA2, 0x55 }, + { WCD939X_DIGITAL_DEM_BYPASS_DATA3, 0x01 }, + { WCD939X_DIGITAL_DEM_SECOND_ORDER, 0x03 }, + { WCD939X_DIGITAL_DSM_CTRL, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_0_STATIC_DATA_3, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_0, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_1, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_2, 0x00 }, + { WCD939X_DIGITAL_DSM_1_STATIC_DATA_3, 0x00 }, + { WCD939X_RX_TOP_PAGE, 0x00 }, + { WCD939X_RX_TOP_TOP_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD939X_RX_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG1, 0x05 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD0_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG1, 0x03 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG2, 0x08 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG3, 0x00 }, + { WCD939X_RX_TOP_DSD1_DEBUG_CFG4, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_CFG1, 0x00 }, + { WCD939X_RX_TOP_PATH_CFG2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHL_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC0, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC1, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC2, 0x00 }, + { WCD939X_RX_TOP_HPHR_PATH_SEC3, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC4, 0x00 }, + { WCD939X_RX_TOP_PATH_SEC5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL0, 0x60 }, + { WCD939X_COMPANDER_HPHL_CTL1, 0xdb }, + { WCD939X_COMPANDER_HPHL_CTL2, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL3, 0x35 }, + { WCD939X_COMPANDER_HPHL_CTL4, 0xff }, + { WCD939X_COMPANDER_HPHL_CTL5, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL7, 0x08 }, + { WCD939X_COMPANDER_HPHL_CTL8, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL9, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL10, 0x06 }, + { WCD939X_COMPANDER_HPHL_CTL11, 0x12 }, + { WCD939X_COMPANDER_HPHL_CTL12, 0x1e }, + { WCD939X_COMPANDER_HPHL_CTL13, 0x2a }, + { WCD939X_COMPANDER_HPHL_CTL14, 0x36 }, + { WCD939X_COMPANDER_HPHL_CTL15, 0x3c }, + { WCD939X_COMPANDER_HPHL_CTL16, 0xc4 }, + { WCD939X_COMPANDER_HPHL_CTL17, 0x00 }, + { WCD939X_COMPANDER_HPHL_CTL18, 0x0c }, + { WCD939X_COMPANDER_HPHL_CTL19, 0x16 }, + { WCD939X_R_CTL0, 0x60 }, + { WCD939X_R_CTL1, 0xdb }, + { WCD939X_R_CTL2, 0xff }, + { WCD939X_R_CTL3, 0x35 }, + { WCD939X_R_CTL4, 0xff }, + { WCD939X_R_CTL5, 0x00 }, + { WCD939X_R_CTL7, 0x08 }, + { WCD939X_R_CTL8, 0x00 }, + { WCD939X_R_CTL9, 0x00 }, + { WCD939X_R_CTL10, 0x06 }, + { WCD939X_R_CTL11, 0x12 }, + { WCD939X_R_CTL12, 0x1e }, + { WCD939X_R_CTL13, 0x2a }, + { WCD939X_R_CTL14, 0x36 }, + { WCD939X_R_CTL15, 0x3c }, + { WCD939X_R_CTL16, 0xc4 }, + { WCD939X_R_CTL17, 0x00 }, + { WCD939X_R_CTL18, 0x0c }, + { WCD939X_R_CTL19, 0x16 }, + { WCD939X_E_PATH_CTL, 0x00 }, + { WCD939X_E_CFG0, 0x07 }, + { WCD939X_E_CFG1, 0x3c }, + { WCD939X_E_CFG2, 0x00 }, + { WCD939X_E_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHL_CFG0, 0x00 }, + { WCD939X_DSD_HPHL_CFG1, 0x00 }, + { WCD939X_DSD_HPHL_CFG2, 0x22 }, + { WCD939X_DSD_HPHL_CFG3, 0x00 }, + { WCD939X_DSD_HPHL_CFG4, 0x1a }, + { WCD939X_DSD_HPHL_CFG5, 0x00 }, + { WCD939X_DSD_HPHR_PATH_CTL, 0x00 }, + { WCD939X_DSD_HPHR_CFG0, 0x00 }, + { WCD939X_DSD_HPHR_CFG1, 0x00 }, + { WCD939X_DSD_HPHR_CFG2, 0x22 }, + { WCD939X_DSD_HPHR_CFG3, 0x00 }, + { WCD939X_DSD_HPHR_CFG4, 0x1a }, + { WCD939X_DSD_HPHR_CFG5, 0x00 }, +}; + +static bool wcd939x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_PAGE: + case WCD939X_ANA_BIAS: + case WCD939X_ANA_RX_SUPPLIES: + case WCD939X_ANA_HPH: + case WCD939X_ANA_EAR: + case WCD939X_ANA_EAR_COMPANDER_CTL: + case WCD939X_ANA_TX_CH1: + case WCD939X_ANA_TX_CH2: + case WCD939X_ANA_TX_CH3: + case WCD939X_ANA_TX_CH4: + case WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD939X_ANA_MICB3_DSP_EN_LOGIC: + case WCD939X_ANA_MBHC_MECH: + case WCD939X_ANA_MBHC_ELECT: + case WCD939X_ANA_MBHC_ZDET: + case WCD939X_ANA_MBHC_BTN0: + case WCD939X_ANA_MBHC_BTN1: + case WCD939X_ANA_MBHC_BTN2: + case WCD939X_ANA_MBHC_BTN3: + case WCD939X_ANA_MBHC_BTN4: + case WCD939X_ANA_MBHC_BTN5: + case WCD939X_ANA_MBHC_BTN6: + case WCD939X_ANA_MBHC_BTN7: + case WCD939X_ANA_MICB1: + case WCD939X_ANA_MICB2: + case WCD939X_ANA_MICB2_RAMP: + case WCD939X_ANA_MICB3: + case WCD939X_ANA_MICB4: + case WCD939X_BIAS_CTL: + case WCD939X_BIAS_VBG_FINE_ADJ: + case WCD939X_LDOL_VDDCX_ADJUST: + case WCD939X_LDOL_DISABLE_LDOL: + case WCD939X_MBHC_CTL_CLK: + case WCD939X_MBHC_CTL_ANA: + case WCD939X_MBHC_ZDET_VNEG_CTL: + case WCD939X_MBHC_ZDET_BIAS_CTL: + case WCD939X_MBHC_CTL_BCS: + case WCD939X_MBHC_TEST_CTL: + case WCD939X_LDOH_MODE: + case WCD939X_LDOH_BIAS: + case WCD939X_LDOH_STB_LOADS: + case WCD939X_LDOH_SLOWRAMP: + case WCD939X_MICB1_TEST_CTL_1: + case WCD939X_MICB1_TEST_CTL_2: + case WCD939X_MICB1_TEST_CTL_3: + case WCD939X_MICB2_TEST_CTL_1: + case WCD939X_MICB2_TEST_CTL_2: + case WCD939X_MICB2_TEST_CTL_3: + case WCD939X_MICB3_TEST_CTL_1: + case WCD939X_MICB3_TEST_CTL_2: + case WCD939X_MICB3_TEST_CTL_3: + case WCD939X_MICB4_TEST_CTL_1: + case WCD939X_MICB4_TEST_CTL_2: + case WCD939X_MICB4_TEST_CTL_3: + case WCD939X_TX_COM_ADC_VCM: + case WCD939X_TX_COM_BIAS_ATEST: + case WCD939X_TX_COM_SPARE1: + case WCD939X_TX_COM_SPARE2: + case WCD939X_TX_COM_TXFE_DIV_CTL: + case WCD939X_TX_COM_TXFE_DIV_START: + case WCD939X_TX_COM_SPARE3: + case WCD939X_TX_COM_SPARE4: + case WCD939X_TX_1_2_TEST_EN: + case WCD939X_TX_1_2_ADC_IB: + case WCD939X_TX_1_2_ATEST_REFCTL: + case WCD939X_TX_1_2_TEST_CTL: + case WCD939X_TX_1_2_TEST_BLK_EN1: + case WCD939X_TX_1_2_TXFE1_CLKDIV: + case WCD939X_TX_3_4_TEST_EN: + case WCD939X_TX_3_4_ADC_IB: + case WCD939X_TX_3_4_ATEST_REFCTL: + case WCD939X_TX_3_4_TEST_CTL: + case WCD939X_TX_3_4_TEST_BLK_EN3: + case WCD939X_TX_3_4_TXFE3_CLKDIV: + case WCD939X_TX_3_4_TEST_BLK_EN2: + case WCD939X_TX_3_4_TXFE2_CLKDIV: + case WCD939X_TX_3_4_SPARE1: + case WCD939X_TX_3_4_TEST_BLK_EN4: + case WCD939X_TX_3_4_TXFE4_CLKDIV: + case WCD939X_TX_3_4_SPARE2: + case WCD939X_CLASSH_MODE_1: + case WCD939X_CLASSH_MODE_2: + case WCD939X_CLASSH_MODE_3: + case WCD939X_CLASSH_CTRL_VCL_1: + case WCD939X_CLASSH_CTRL_VCL_2: + case WCD939X_CLASSH_CTRL_CCL_1: + case WCD939X_CLASSH_CTRL_CCL_2: + case WCD939X_CLASSH_CTRL_CCL_3: + case WCD939X_CLASSH_CTRL_CCL_4: + case WCD939X_CLASSH_CTRL_CCL_5: + case WCD939X_CLASSH_BUCK_TMUX_A_D: + case WCD939X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD939X_CLASSH_SPARE: + case WCD939X_FLYBACK_EN: + case WCD939X_FLYBACK_VNEG_CTRL_1: + case WCD939X_FLYBACK_VNEG_CTRL_2: + case WCD939X_FLYBACK_VNEG_CTRL_3: + case WCD939X_FLYBACK_VNEG_CTRL_4: + case WCD939X_FLYBACK_VNEG_CTRL_5: + case WCD939X_FLYBACK_VNEG_CTRL_6: + case WCD939X_FLYBACK_VNEG_CTRL_7: + case WCD939X_FLYBACK_VNEG_CTRL_8: + case WCD939X_FLYBACK_VNEG_CTRL_9: + case WCD939X_FLYBACK_VNEGDAC_CTRL_1: + case WCD939X_FLYBACK_VNEGDAC_CTRL_2: + case WCD939X_FLYBACK_VNEGDAC_CTRL_3: + case WCD939X_FLYBACK_CTRL_1: + case WCD939X_FLYBACK_TEST_CTL: + case WCD939X_RX_AUX_SW_CTL: + case WCD939X_RX_PA_AUX_IN_CONN: + case WCD939X_RX_TIMER_DIV: + case WCD939X_RX_OCP_CTL: + case WCD939X_RX_OCP_COUNT: + case WCD939X_RX_BIAS_EAR_DAC: + case WCD939X_RX_BIAS_EAR_AMP: + case WCD939X_RX_BIAS_HPH_LDO: + case WCD939X_RX_BIAS_HPH_PA: + case WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD939X_RX_BIAS_HPH_RDAC_LDO: + case WCD939X_RX_BIAS_HPH_CNP1: + case WCD939X_RX_BIAS_HPH_LOWPOWER: + case WCD939X_RX_BIAS_AUX_DAC: + case WCD939X_RX_BIAS_AUX_AMP: + case WCD939X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD939X_RX_BIAS_MISC: + case WCD939X_RX_BIAS_BUCK_RST: + case WCD939X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD939X_RX_BIAS_FLYB_ERRAMP: + case WCD939X_RX_BIAS_FLYB_BUFF: + case WCD939X_RX_BIAS_FLYB_MID_RST: + case WCD939X_HPH_CNP_EN: + case WCD939X_HPH_CNP_WG_CTL: + case WCD939X_HPH_CNP_WG_TIME: + case WCD939X_HPH_OCP_CTL: + case WCD939X_HPH_AUTO_CHOP: + case WCD939X_HPH_CHOP_CTL: + case WCD939X_HPH_PA_CTL1: + case WCD939X_HPH_PA_CTL2: + case WCD939X_HPH_L_EN: + case WCD939X_HPH_L_TEST: + case WCD939X_HPH_L_ATEST: + case WCD939X_HPH_R_EN: + case WCD939X_HPH_R_TEST: + case WCD939X_HPH_R_ATEST: + case WCD939X_HPH_RDAC_CLK_CTL1: + case WCD939X_HPH_RDAC_CLK_CTL2: + case WCD939X_HPH_RDAC_LDO_CTL: + case WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD939X_HPH_REFBUFF_UHQA_CTL: + case WCD939X_HPH_REFBUFF_LP_CTL: + case WCD939X_HPH_L_DAC_CTL: + case WCD939X_HPH_R_DAC_CTL: + case WCD939X_HPH_SURGE_COMP_SEL: + case WCD939X_HPH_SURGE_EN: + case WCD939X_HPH_SURGE_MISC1: + case WCD939X_EAR_EN: + case WCD939X_EAR_PA_CON: + case WCD939X_EAR_SP_CON: + case WCD939X_EAR_DAC_CON: + case WCD939X_EAR_CNP_FSM_CON: + case WCD939X_EAR_TEST_CTL: + case WCD939X_FLYBACK_NEW_CTRL_2: + case WCD939X_FLYBACK_NEW_CTRL_3: + case WCD939X_FLYBACK_NEW_CTRL_4: + case WCD939X_ANA_NEW_PAGE: + case WCD939X_HPH_NEW_ANA_HPH2: + case WCD939X_HPH_NEW_ANA_HPH3: + case WCD939X_SLEEP_CTL: + case WCD939X_SLEEP_WATCHDOG_CTL: + case WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD939X_MBHC_NEW_CTL_1: + case WCD939X_MBHC_NEW_CTL_2: + case WCD939X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD939X_MBHC_NEW_ZDET_ANA_CTL: + case WCD939X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD939X_TX_NEW_CH12_MUX: + case WCD939X_TX_NEW_CH34_MUX: + case WCD939X_DIE_CRACK_DET_EN: + case WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R: + case WCD939X_HPH_NEW_INT_PA_MISC1: + case WCD939X_HPH_NEW_INT_PA_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD939X_HPH_NEW_INT_TIMER1: + case WCD939X_HPH_NEW_INT_TIMER2: + case WCD939X_HPH_NEW_INT_TIMER3: + case WCD939X_HPH_NEW_INT_TIMER4: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD939X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW: + case WCD939X_EAR_INT_NEW_CHOPPER_CON: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD939X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD939X_EAR_INT_NEW_DYNAMIC_BIAS: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD939X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD939X_DIE_CRACK_INT_DET_INT1: + case WCD939X_DIE_CRACK_INT_DET_INT2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M: + case WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0: + case WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1: + case WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L2: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L1: + case WCD939X_TX_COM_NEW_INT_ADC_INT_L0: + case WCD939X_TX_COM_NEW_INT_ADC_INT_ULP: + case WCD939X_DIGITAL_PAGE: + case WCD939X_DIGITAL_SWR_TX_CLK_RATE: + case WCD939X_DIGITAL_CDC_RST_CTL: + case WCD939X_DIGITAL_TOP_CLK_CFG: + case WCD939X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD939X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD939X_DIGITAL_SWR_RST_EN: + case WCD939X_DIGITAL_CDC_PATH_MODE: + case WCD939X_DIGITAL_CDC_RX_RST: + case WCD939X_DIGITAL_CDC_RX0_CTL: + case WCD939X_DIGITAL_CDC_RX1_CTL: + case WCD939X_DIGITAL_CDC_RX2_CTL: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1: + case WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3: + case WCD939X_DIGITAL_CDC_COMP_CTL_0: + case WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD939X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R1: + case WCD939X_DIGITAL_CDC_HPH_DSM_R2: + case WCD939X_DIGITAL_CDC_HPH_DSM_R3: + case WCD939X_DIGITAL_CDC_HPH_DSM_R4: + case WCD939X_DIGITAL_CDC_HPH_DSM_R5: + case WCD939X_DIGITAL_CDC_HPH_DSM_R6: + case WCD939X_DIGITAL_CDC_HPH_DSM_R7: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A1_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A2_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A3_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A4_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A5_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_A6_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_A7_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_0: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_1: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_2: + case WCD939X_DIGITAL_CDC_EAR_DSM_C_3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R1: + case WCD939X_DIGITAL_CDC_EAR_DSM_R2: + case WCD939X_DIGITAL_CDC_EAR_DSM_R3: + case WCD939X_DIGITAL_CDC_EAR_DSM_R4: + case WCD939X_DIGITAL_CDC_EAR_DSM_R5: + case WCD939X_DIGITAL_CDC_EAR_DSM_R6: + case WCD939X_DIGITAL_CDC_EAR_DSM_R7: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1: + case WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2: + case WCD939X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_GAIN_CTL: + case WCD939X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD939X_DIGITAL_CDC_SWR_CLH: + case WCD939X_DIGITAL_SWR_CLH_BYP: + case WCD939X_DIGITAL_CDC_TX0_CTL: + case WCD939X_DIGITAL_CDC_TX1_CTL: + case WCD939X_DIGITAL_CDC_TX2_CTL: + case WCD939X_DIGITAL_CDC_TX_RST: + case WCD939X_DIGITAL_CDC_REQ_CTL: + case WCD939X_DIGITAL_CDC_RST: + case WCD939X_DIGITAL_CDC_AMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC_CTL: + case WCD939X_DIGITAL_CDC_DMIC1_CTL: + case WCD939X_DIGITAL_CDC_DMIC2_CTL: + case WCD939X_DIGITAL_CDC_DMIC3_CTL: + case WCD939X_DIGITAL_CDC_DMIC4_CTL: + case WCD939X_DIGITAL_EFUSE_PRG_CTL: + case WCD939X_DIGITAL_EFUSE_CTL: + case WCD939X_DIGITAL_CDC_DMIC_RATE_1_2: + case WCD939X_DIGITAL_CDC_DMIC_RATE_3_4: + case WCD939X_DIGITAL_PDM_WD_CTL0: + case WCD939X_DIGITAL_PDM_WD_CTL1: + case WCD939X_DIGITAL_PDM_WD_CTL2: + case WCD939X_DIGITAL_INTR_MODE: + case WCD939X_DIGITAL_INTR_MASK_0: + case WCD939X_DIGITAL_INTR_MASK_1: + case WCD939X_DIGITAL_INTR_MASK_2: + case WCD939X_DIGITAL_INTR_CLEAR_0: + case WCD939X_DIGITAL_INTR_CLEAR_1: + case WCD939X_DIGITAL_INTR_CLEAR_2: + case WCD939X_DIGITAL_INTR_LEVEL_0: + case WCD939X_DIGITAL_INTR_LEVEL_1: + case WCD939X_DIGITAL_INTR_LEVEL_2: + case WCD939X_DIGITAL_INTR_SET_0: + case WCD939X_DIGITAL_INTR_SET_1: + case WCD939X_DIGITAL_INTR_SET_2: + case WCD939X_DIGITAL_INTR_TEST_0: + case WCD939X_DIGITAL_INTR_TEST_1: + case WCD939X_DIGITAL_INTR_TEST_2: + case WCD939X_DIGITAL_TX_MODE_DBG_EN: + case WCD939X_DIGITAL_TX_MODE_DBG_0_1: + case WCD939X_DIGITAL_TX_MODE_DBG_2_3: + case WCD939X_DIGITAL_LB_IN_SEL_CTL: + case WCD939X_DIGITAL_LOOP_BACK_MODE: + case WCD939X_DIGITAL_SWR_DAC_TEST: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD939X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD939X_DIGITAL_SWR_HM_TEST_TX_2: + case WCD939X_DIGITAL_PAD_CTL_SWR_0: + case WCD939X_DIGITAL_PAD_CTL_SWR_1: + case WCD939X_DIGITAL_I2C_CTL: + case WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD939X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD939X_DIGITAL_PAD_CTL_PDM_TX2: + case WCD939X_DIGITAL_PAD_INP_DIS_0: + case WCD939X_DIGITAL_PAD_INP_DIS_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_0: + case WCD939X_DIGITAL_DRIVE_STRENGTH_1: + case WCD939X_DIGITAL_DRIVE_STRENGTH_2: + case WCD939X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD939X_DIGITAL_GPIO_MODE: + case WCD939X_DIGITAL_PIN_CTL_OE: + case WCD939X_DIGITAL_PIN_CTL_DATA_0: + case WCD939X_DIGITAL_PIN_CTL_DATA_1: + case WCD939X_DIGITAL_DIG_DEBUG_CTL: + case WCD939X_DIGITAL_DIG_DEBUG_EN: + case WCD939X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD939X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD939X_DIGITAL_SSP_DBG: + case WCD939X_DIGITAL_SPARE_0: + case WCD939X_DIGITAL_SPARE_1: + case WCD939X_DIGITAL_SPARE_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_0: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_1: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_2: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_3: + case WCD939X_DIGITAL_TX_REQ_FB_CTL_4: + case WCD939X_DIGITAL_DEM_BYPASS_DATA0: + case WCD939X_DIGITAL_DEM_BYPASS_DATA1: + case WCD939X_DIGITAL_DEM_BYPASS_DATA2: + case WCD939X_DIGITAL_DEM_BYPASS_DATA3: + case WCD939X_DIGITAL_DEM_SECOND_ORDER: + case WCD939X_DIGITAL_DSM_CTRL: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_0_STATIC_DATA_3: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_0: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_1: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_2: + case WCD939X_DIGITAL_DSM_1_STATIC_DATA_3: + case WCD939X_RX_TOP_PAGE: + case WCD939X_RX_TOP_TOP_CFG0: + case WCD939X_RX_TOP_HPHL_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHL_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHL_COMP_LUT: + case WCD939X_RX_TOP_HPHR_COMP_WR_LSB: + case WCD939X_RX_TOP_HPHR_COMP_WR_MSB: + case WCD939X_RX_TOP_HPHR_COMP_LUT: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG4: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG1: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG2: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG3: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG4: + case WCD939X_RX_TOP_HPHL_PATH_CFG0: + case WCD939X_RX_TOP_HPHL_PATH_CFG1: + case WCD939X_RX_TOP_HPHR_PATH_CFG0: + case WCD939X_RX_TOP_HPHR_PATH_CFG1: + case WCD939X_RX_TOP_PATH_CFG2: + case WCD939X_RX_TOP_HPHL_PATH_SEC0: + case WCD939X_RX_TOP_HPHL_PATH_SEC1: + case WCD939X_RX_TOP_HPHL_PATH_SEC2: + case WCD939X_RX_TOP_HPHL_PATH_SEC3: + case WCD939X_RX_TOP_HPHR_PATH_SEC0: + case WCD939X_RX_TOP_HPHR_PATH_SEC1: + case WCD939X_RX_TOP_HPHR_PATH_SEC2: + case WCD939X_RX_TOP_HPHR_PATH_SEC3: + case WCD939X_RX_TOP_PATH_SEC4: + case WCD939X_RX_TOP_PATH_SEC5: + case WCD939X_COMPANDER_HPHL_CTL0: + case WCD939X_COMPANDER_HPHL_CTL1: + case WCD939X_COMPANDER_HPHL_CTL2: + case WCD939X_COMPANDER_HPHL_CTL3: + case WCD939X_COMPANDER_HPHL_CTL4: + case WCD939X_COMPANDER_HPHL_CTL5: + case WCD939X_COMPANDER_HPHL_CTL7: + case WCD939X_COMPANDER_HPHL_CTL8: + case WCD939X_COMPANDER_HPHL_CTL9: + case WCD939X_COMPANDER_HPHL_CTL10: + case WCD939X_COMPANDER_HPHL_CTL11: + case WCD939X_COMPANDER_HPHL_CTL12: + case WCD939X_COMPANDER_HPHL_CTL13: + case WCD939X_COMPANDER_HPHL_CTL14: + case WCD939X_COMPANDER_HPHL_CTL15: + case WCD939X_COMPANDER_HPHL_CTL16: + case WCD939X_COMPANDER_HPHL_CTL17: + case WCD939X_COMPANDER_HPHL_CTL18: + case WCD939X_COMPANDER_HPHL_CTL19: + case WCD939X_R_CTL0: + case WCD939X_R_CTL1: + case WCD939X_R_CTL2: + case WCD939X_R_CTL3: + case WCD939X_R_CTL4: + case WCD939X_R_CTL5: + case WCD939X_R_CTL7: + case WCD939X_R_CTL8: + case WCD939X_R_CTL9: + case WCD939X_R_CTL10: + case WCD939X_R_CTL11: + case WCD939X_R_CTL12: + case WCD939X_R_CTL13: + case WCD939X_R_CTL14: + case WCD939X_R_CTL15: + case WCD939X_R_CTL16: + case WCD939X_R_CTL17: + case WCD939X_R_CTL18: + case WCD939X_R_CTL19: + case WCD939X_E_PATH_CTL: + case WCD939X_E_CFG0: + case WCD939X_E_CFG1: + case WCD939X_E_CFG2: + case WCD939X_E_CFG3: + case WCD939X_DSD_HPHL_PATH_CTL: + case WCD939X_DSD_HPHL_CFG0: + case WCD939X_DSD_HPHL_CFG1: + case WCD939X_DSD_HPHL_CFG2: + case WCD939X_DSD_HPHL_CFG3: + case WCD939X_DSD_HPHL_CFG4: + case WCD939X_DSD_HPHL_CFG5: + case WCD939X_DSD_HPHR_PATH_CTL: + case WCD939X_DSD_HPHR_CFG0: + case WCD939X_DSD_HPHR_CFG1: + case WCD939X_DSD_HPHR_CFG2: + case WCD939X_DSD_HPHR_CFG3: + case WCD939X_DSD_HPHR_CFG4: + case WCD939X_DSD_HPHR_CFG5: + return true; + } + + return false; +} + +static bool wcd939x_readable_register(struct device *dev, unsigned int reg) +{ + /* Read-Only Registers */ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_CHIP_ID0: + case WCD939X_DIGITAL_CHIP_ID1: + case WCD939X_DIGITAL_CHIP_ID2: + case WCD939X_DIGITAL_CHIP_ID3: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_EFUSE_T_DATA_0: + case WCD939X_DIGITAL_EFUSE_T_DATA_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_DIGITAL_EFUSE_REG_0: + case WCD939X_DIGITAL_EFUSE_REG_1: + case WCD939X_DIGITAL_EFUSE_REG_2: + case WCD939X_DIGITAL_EFUSE_REG_3: + case WCD939X_DIGITAL_EFUSE_REG_4: + case WCD939X_DIGITAL_EFUSE_REG_5: + case WCD939X_DIGITAL_EFUSE_REG_6: + case WCD939X_DIGITAL_EFUSE_REG_7: + case WCD939X_DIGITAL_EFUSE_REG_8: + case WCD939X_DIGITAL_EFUSE_REG_9: + case WCD939X_DIGITAL_EFUSE_REG_10: + case WCD939X_DIGITAL_EFUSE_REG_11: + case WCD939X_DIGITAL_EFUSE_REG_12: + case WCD939X_DIGITAL_EFUSE_REG_13: + case WCD939X_DIGITAL_EFUSE_REG_14: + case WCD939X_DIGITAL_EFUSE_REG_15: + case WCD939X_DIGITAL_EFUSE_REG_16: + case WCD939X_DIGITAL_EFUSE_REG_17: + case WCD939X_DIGITAL_EFUSE_REG_18: + case WCD939X_DIGITAL_EFUSE_REG_19: + case WCD939X_DIGITAL_EFUSE_REG_20: + case WCD939X_DIGITAL_EFUSE_REG_21: + case WCD939X_DIGITAL_EFUSE_REG_22: + case WCD939X_DIGITAL_EFUSE_REG_23: + case WCD939X_DIGITAL_EFUSE_REG_24: + case WCD939X_DIGITAL_EFUSE_REG_25: + case WCD939X_DIGITAL_EFUSE_REG_26: + case WCD939X_DIGITAL_EFUSE_REG_27: + case WCD939X_DIGITAL_EFUSE_REG_28: + case WCD939X_DIGITAL_EFUSE_REG_29: + case WCD939X_DIGITAL_EFUSE_REG_30: + case WCD939X_DIGITAL_EFUSE_REG_31: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + + return wcd939x_rdwr_register(dev, reg); +} + +static bool wcd939x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD939X_ANA_MBHC_RESULT_1: + case WCD939X_ANA_MBHC_RESULT_2: + case WCD939X_ANA_MBHC_RESULT_3: + case WCD939X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD939X_TX_1_2_SAR2_ERR: + case WCD939X_TX_1_2_SAR1_ERR: + case WCD939X_TX_3_4_SAR4_ERR: + case WCD939X_TX_3_4_SAR3_ERR: + case WCD939X_HPH_L_STATUS: + case WCD939X_HPH_R_STATUS: + case WCD939X_HPH_SURGE_STATUS: + case WCD939X_EAR_STATUS_REG_1: + case WCD939X_EAR_STATUS_REG_2: + case WCD939X_MBHC_NEW_FSM_STATUS: + case WCD939X_MBHC_NEW_ADC_RESULT: + case WCD939X_DIE_CRACK_DET_OUT: + case WCD939X_DIGITAL_INTR_STATUS_0: + case WCD939X_DIGITAL_INTR_STATUS_1: + case WCD939X_DIGITAL_INTR_STATUS_2: + case WCD939X_DIGITAL_SWR_HM_TEST_0: + case WCD939X_DIGITAL_SWR_HM_TEST_1: + case WCD939X_DIGITAL_PIN_STATUS_0: + case WCD939X_DIGITAL_PIN_STATUS_1: + case WCD939X_DIGITAL_MODE_STATUS_0: + case WCD939X_DIGITAL_MODE_STATUS_1: + case WCD939X_RX_TOP_HPHL_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHL_COMP_RD_MSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_LSB: + case WCD939X_RX_TOP_HPHR_COMP_RD_MSB: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD0_DEBUG_CFG6: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG5: + case WCD939X_RX_TOP_DSD1_DEBUG_CFG6: + case WCD939X_COMPANDER_HPHL_CTL6: + case WCD939X_R_CTL6: + return true; + } + return false; +} + +static bool wcd939x_writeable_register(struct device *dev, unsigned int reg) +{ + return wcd939x_rdwr_register(dev, reg); +} + +static const struct regmap_config wcd939x_regmap_config = { + .name = "wcd939x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd939x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd939x_defaults), + .max_register = WCD939X_MAX_REGISTER, + .readable_reg = wcd939x_readable_register, + .writeable_reg = wcd939x_writeable_register, + .volatile_reg = wcd939x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9390_slave_ops = { + .update_status = wcd9390_update_status, + .interrupt_callback = wcd9390_interrupt_callback, + .bus_config = wcd9390_bus_config, +}; + +static int wcd939x_sdw_component_bind(struct device *dev, struct device *master, + void *data) +{ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + +static void wcd939x_sdw_component_unbind(struct device *dev, + struct device *master, void *data) +{ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); +} + +static const struct component_ops wcd939x_sdw_component_ops = { + .bind = wcd939x_sdw_component_bind, + .unbind = wcd939x_sdw_component_unbind, +}; + +static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /* + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + wcd->is_tx = true; + ret = of_property_read_u32_array(dev->of_node, + "qcom,tx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, + "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD939X_MAX_RX_SWR_PORTS); + } + + if (ret < 0) + dev_info(dev, "Static Port mapping not specified\n"); + + wcd->sdev = pdev; + dev_set_drvdata(dev, wcd); + + pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | + SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD939X_MAX_TX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd939x_tx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + } else { + pdev->prop.sink_ports = GENMASK(WCD939X_MAX_RX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd939x_rx_dpn_prop; + wcd->ch_info = &wcd939x_sdw_rx_ch_info[0]; + } + + if (wcd->is_tx) { + /* + * Do not use devres here since devres_release_group() could + * be called by component_unbind() id the aggregate device + * fails to bind. + */ + wcd->regmap = regmap_init_sdw(pdev, &wcd939x_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Regmap init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wcd->regmap, true); + } + + ret = component_add(dev, &wcd939x_sdw_component_ops); + if (ret) + return ret; + + /* Set suspended until aggregate device is bind */ + pm_runtime_set_suspended(dev); + + return 0; +} + +static int wcd9390_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + component_del(dev, &wcd939x_sdw_component_ops); + + if (wcd->regmap) + regmap_exit(wcd->regmap); + + return 0; +} + +static const struct sdw_device_id wcd9390_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10e, 0), /* WCD9390 & WCD9390 RX/TX Device ID */ + {}, +}; +MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id); + +static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + } + + return 0; +} + +static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev) +{ + struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); + regcache_sync(wcd->regmap); + } + + return 0; +} + +static const struct dev_pm_ops wcd939x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL) +}; + +static struct sdw_driver wcd9390_codec_driver = { + .probe = wcd9390_probe, + .remove = wcd9390_remove, + .ops = &wcd9390_slave_ops, + .id_table = wcd9390_slave_id, + .driver = { + .name = "wcd9390-codec", + .pm = &wcd939x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9390_codec_driver); + +MODULE_DESCRIPTION("WCD939X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h new file mode 100644 index 000000000000..807cf3113d20 --- /dev/null +++ b/sound/soc/codecs/wcd939x.h @@ -0,0 +1,989 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __WCD939X_H__ +#define __WCD939X_H__ +#include +#include + +#define WCD939X_BASE (0x3000) +#define WCD939X_ANA_PAGE (0x3000) +#define WCD939X_ANA_BIAS (0x3001) +#define WCD939X_BIAS_ANALOG_BIAS_EN BIT(7) +#define WCD939X_BIAS_PRECHRG_EN BIT(6) +#define WCD939X_BIAS_PRECHRG_CTL_MODE BIT(5) +#define WCD939X_ANA_RX_SUPPLIES (0x3008) +#define WCD939X_RX_SUPPLIES_VPOS_EN BIT(7) +#define WCD939X_RX_SUPPLIES_VNEG_EN BIT(6) +#define WCD939X_RX_SUPPLIES_VPOS_PWR_LVL BIT(3) +#define WCD939X_RX_SUPPLIES_VNEG_PWR_LVL BIT(2) +#define WCD939X_RX_SUPPLIES_REGULATOR_MODE BIT(1) +#define WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE BIT(0) +#define WCD939X_ANA_HPH (0x3009) +#define WCD939X_HPH_HPHL_ENABLE BIT(7) +#define WCD939X_HPH_HPHR_ENABLE BIT(6) +#define WCD939X_HPH_HPHL_REF_ENABLE BIT(5) +#define WCD939X_HPH_HPHR_REF_ENABLE BIT(4) +#define WCD939X_HPH_PWR_LEVEL GENMASK(3, 2) +#define WCD939X_ANA_EAR (0x300a) +#define WCD939X_ANA_EAR_COMPANDER_CTL (0x300b) +#define WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG BIT(7) +#define WCD939X_EAR_COMPANDER_CTL_EAR_GAIN GENMASK(6, 2) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_BYP BIT(1) +#define WCD939X_EAR_COMPANDER_CTL_COMP_DFF_CLK_EDGE BIT(0) +#define WCD939X_ANA_TX_CH1 (0x300e) +#define WCD939X_ANA_TX_CH2 (0x300f) +#define WCD939X_TX_CH2_ENABLE BIT(7) +#define WCD939X_TX_CH2_HPF1_INIT BIT(6) +#define WCD939X_TX_CH2_HPF2_INIT BIT(5) +#define WCD939X_TX_CH2_GAIN GENMASK(4, 0) +#define WCD939X_ANA_TX_CH3 (0x3010) +#define WCD939X_ANA_TX_CH4 (0x3011) +#define WCD939X_TX_CH4_ENABLE BIT(7) +#define WCD939X_TX_CH4_HPF3_INIT BIT(6) +#define WCD939X_TX_CH4_HPF4_INIT BIT(5) +#define WCD939X_TX_CH4_GAIN GENMASK(4, 0) +#define WCD939X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012) +#define WCD939X_ANA_MICB3_DSP_EN_LOGIC (0x3013) +#define WCD939X_ANA_MBHC_MECH (0x3014) +#define WCD939X_MBHC_MECH_L_DET_EN BIT(7) +#define WCD939X_MBHC_MECH_GND_DET_EN BIT(6) +#define WCD939X_MBHC_MECH_MECH_DETECT_TYPE BIT(5) +#define WCD939X_MBHC_MECH_HPHL_PLUG_TYPE BIT(4) +#define WCD939X_MBHC_MECH_GND_PLUG_TYPE BIT(3) +#define WCD939X_MBHC_MECH_MECH_HS_L_PULLUP_COMP_EN BIT(2) +#define WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN BIT(1) +#define WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND BIT(0) +#define WCD939X_ANA_MBHC_ELECT (0x3015) +#define WCD939X_MBHC_ELECT_FSM_EN BIT(7) +#define WCD939X_MBHC_ELECT_BTNDET_ISRC_CTL GENMASK(6, 4) +#define WCD939X_MBHC_ELECT_ELECT_DET_TYPE BIT(3) +#define WCD939X_MBHC_ELECT_ELECT_SCHMT_ISRC_CTL GENMASK(2, 1) +#define WCD939X_MBHC_ELECT_BIAS_EN BIT(0) +#define WCD939X_ANA_MBHC_ZDET (0x3016) +#define WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN BIT(7) +#define WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN BIT(6) +#define WCD939X_MBHC_ZDET_ZDET_CHG_EN BIT(5) +#define WCD939X_MBHC_ZDET_ZDET_ILEAK_COMP_EN BIT(4) +#define WCD939X_MBHC_ZDET_ELECT_ISRC_EN BIT(1) +#define WCD939X_ANA_MBHC_RESULT_1 (0x3017) +#define WCD939X_MBHC_RESULT_1_Z_RESULT_LSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_2 (0x3018) +#define WCD939X_MBHC_RESULT_2_Z_RESULT_MSB GENMASK(7, 0) +#define WCD939X_ANA_MBHC_RESULT_3 (0x3019) +#define WCD939X_ANA_MBHC_BTN0 (0x301a) +#define WCD939X_MBHC_BTN0_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN1 (0x301b) +#define WCD939X_MBHC_BTN1_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN2 (0x301c) +#define WCD939X_MBHC_BTN2_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN3 (0x301d) +#define WCD939X_MBHC_BTN3_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN4 (0x301e) +#define WCD939X_MBHC_BTN4_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN5 (0x301f) +#define WCD939X_MBHC_BTN5_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN6 (0x3020) +#define WCD939X_MBHC_BTN6_VTH GENMASK(7, 2) +#define WCD939X_ANA_MBHC_BTN7 (0x3021) +#define WCD939X_MBHC_BTN7_VTH GENMASK(7, 2) +#define WCD939X_ANA_MICB1 (0x3022) +#define WCD939X_MICB1_ENABLE GENMASK(7, 6) +#define WCD939X_MICB1_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2 (0x3023) +#define WCD939X_MICB2_ENABLE GENMASK(7, 6) +#define WCD939X_MICB2_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB2_RAMP (0x3024) +#define WCD939X_MICB2_RAMP_RAMP_ENABLE BIT(7) +#define WCD939X_MICB2_RAMP_MB2_IN2P_SHORT_ENABLE BIT(6) +#define WCD939X_MICB2_RAMP_ALLSW_OVRD_ENABLE BIT(5) +#define WCD939X_MICB2_RAMP_SHIFT_CTL GENMASK(4, 2) +#define WCD939X_MICB2_RAMP_USB_MGDET_MICB2_RAMP GENMASK(1, 0) +#define WCD939X_ANA_MICB3 (0x3025) +#define WCD939X_MICB3_ENABLE GENMASK(7, 6) +#define WCD939X_MICB3_VOUT_CTL GENMASK(5, 0) +#define WCD939X_ANA_MICB4 (0x3026) +#define WCD939X_MICB4_ENABLE GENMASK(7, 6) +#define WCD939X_MICB4_VOUT_CTL GENMASK(5, 0) +#define WCD939X_BIAS_CTL (0x3028) +#define WCD939X_BIAS_VBG_FINE_ADJ (0x3029) +#define WCD939X_LDOL_VDDCX_ADJUST (0x3040) +#define WCD939X_LDOL_DISABLE_LDOL (0x3041) +#define WCD939X_MBHC_CTL_CLK (0x3056) +#define WCD939X_MBHC_CTL_ANA (0x3057) +#define WCD939X_MBHC_ZDET_VNEG_CTL (0x3058) +#define WCD939X_MBHC_ZDET_BIAS_CTL (0x3059) +#define WCD939X_MBHC_CTL_BCS (0x305a) +#define WCD939X_MBHC_MOISTURE_DET_FSM_STATUS (0x305b) +#define WCD939X_MBHC_TEST_CTL (0x305c) +#define WCD939X_LDOH_MODE (0x3067) +#define WCD939X_MODE_LDOH_EN BIT(7) +#define WCD939X_MODE_PWRDN_STATE BIT(6) +#define WCD939X_MODE_SLOWRAMP_EN BIT(5) +#define WCD939X_MODE_VOUT_ADJUST GENMASK(4, 3) +#define WCD939X_MODE_VOUT_COARSE_ADJ GENMASK(2, 0) +#define WCD939X_LDOH_BIAS (0x3068) +#define WCD939X_LDOH_STB_LOADS (0x3069) +#define WCD939X_LDOH_SLOWRAMP (0x306a) +#define WCD939X_MICB1_TEST_CTL_1 (0x306b) +#define WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL GENMASK(7, 5) +#define WCD939X_TEST_CTL_1_EN_VREFGEN BIT(4) +#define WCD939X_TEST_CTL_1_EN_LDO BIT(3) +#define WCD939X_TEST_CTL_1_LDO_BLEEDER_I_CTRL GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_2 (0x306c) +#define WCD939X_TEST_CTL_2_IBIAS_VREFGEN GENMASK(7, 6) +#define WCD939X_TEST_CTL_2_INRUSH_CURRENT_FIX_DIS BIT(5) +#define WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER GENMASK(2, 0) +#define WCD939X_MICB1_TEST_CTL_3 (0x306d) +#define WCD939X_TEST_CTL_3_CFILT_REF_EN BIT(7) +#define WCD939X_TEST_CTL_3_RZ_LDO_VAL GENMASK(6, 4) +#define WCD939X_TEST_CTL_3_IBIAS_LDO_STG3 GENMASK(3, 2) +#define WCD939X_TEST_CTL_3_ATEST_CTRL GENMASK(1, 0) +#define WCD939X_MICB2_TEST_CTL_1 (0x306e) +#define WCD939X_MICB2_TEST_CTL_2 (0x306f) +#define WCD939X_MICB2_TEST_CTL_3 (0x3070) +#define WCD939X_MICB3_TEST_CTL_1 (0x3071) +#define WCD939X_MICB3_TEST_CTL_2 (0x3072) +#define WCD939X_MICB3_TEST_CTL_3 (0x3073) +#define WCD939X_MICB4_TEST_CTL_1 (0x3074) +#define WCD939X_MICB4_TEST_CTL_2 (0x3075) +#define WCD939X_MICB4_TEST_CTL_3 (0x3076) +#define WCD939X_TX_COM_ADC_VCM (0x3077) +#define WCD939X_TX_COM_BIAS_ATEST (0x3078) +#define WCD939X_TX_COM_SPARE1 (0x3079) +#define WCD939X_TX_COM_SPARE2 (0x307a) +#define WCD939X_TX_COM_TXFE_DIV_CTL (0x307b) +#define WCD939X_TX_COM_TXFE_DIV_START (0x307c) +#define WCD939X_TX_COM_SPARE3 (0x307d) +#define WCD939X_TX_COM_SPARE4 (0x307e) +#define WCD939X_TX_1_2_TEST_EN (0x307f) +#define WCD939X_TX_1_2_ADC_IB (0x3080) +#define WCD939X_TX_1_2_ATEST_REFCTL (0x3081) +#define WCD939X_TX_1_2_TEST_CTL (0x3082) +#define WCD939X_TX_1_2_TEST_BLK_EN1 (0x3083) +#define WCD939X_TX_1_2_TXFE1_CLKDIV (0x3084) +#define WCD939X_TX_1_2_SAR2_ERR (0x3085) +#define WCD939X_TX_1_2_SAR1_ERR (0x3086) +#define WCD939X_TX_3_4_TEST_EN (0x3087) +#define WCD939X_TX_3_4_ADC_IB (0x3088) +#define WCD939X_TX_3_4_ATEST_REFCTL (0x3089) +#define WCD939X_TX_3_4_TEST_CTL (0x308a) +#define WCD939X_TX_3_4_TEST_BLK_EN3 (0x308b) +#define WCD939X_TX_3_4_TXFE3_CLKDIV (0x308c) +#define WCD939X_TX_3_4_SAR4_ERR (0x308d) +#define WCD939X_TX_3_4_SAR3_ERR (0x308e) +#define WCD939X_TX_3_4_TEST_BLK_EN2 (0x308f) +#define WCD939X_TEST_BLK_EN2_ADC2_INT1_EN BIT(7) +#define WCD939X_TEST_BLK_EN2_ADC2_INT2_EN BIT(6) +#define WCD939X_TEST_BLK_EN2_ADC2_SAR_EN BIT(5) +#define WCD939X_TEST_BLK_EN2_ADC2_CMGEN_EN BIT(4) +#define WCD939X_TEST_BLK_EN2_ADC2_CLKGEN_EN BIT(3) +#define WCD939X_TEST_BLK_EN2_ADC12_VREF_NONL2 GENMASK(2, 1) +#define WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN BIT(0) +#define WCD939X_TX_3_4_TXFE2_CLKDIV (0x3090) +#define WCD939X_TX_3_4_SPARE1 (0x3091) +#define WCD939X_TX_3_4_TEST_BLK_EN4 (0x3092) +#define WCD939X_TX_3_4_TXFE4_CLKDIV (0x3093) +#define WCD939X_TX_3_4_SPARE2 (0x3094) +#define WCD939X_CLASSH_MODE_1 (0x3097) +#define WCD939X_CLASSH_MODE_2 (0x3098) +#define WCD939X_CLASSH_MODE_3 (0x3099) +#define WCD939X_CLASSH_CTRL_VCL_1 (0x309a) +#define WCD939X_CLASSH_CTRL_VCL_2 (0x309b) +#define WCD939X_CLASSH_CTRL_CCL_1 (0x309c) +#define WCD939X_CLASSH_CTRL_CCL_2 (0x309d) +#define WCD939X_CLASSH_CTRL_CCL_3 (0x309e) +#define WCD939X_CLASSH_CTRL_CCL_4 (0x309f) +#define WCD939X_CLASSH_CTRL_CCL_5 (0x30a0) +#define WCD939X_CLASSH_BUCK_TMUX_A_D (0x30a1) +#define WCD939X_CLASSH_BUCK_SW_DRV_CNTL (0x30a2) +#define WCD939X_CLASSH_SPARE (0x30a3) +#define WCD939X_FLYBACK_EN (0x30a4) +#define WCD939X_FLYBACK_VNEG_CTRL_1 (0x30a5) +#define WCD939X_FLYBACK_VNEG_CTRL_2 (0x30a6) +#define WCD939X_FLYBACK_VNEG_CTRL_3 (0x30a7) +#define WCD939X_FLYBACK_VNEG_CTRL_4 (0x30a8) +#define WCD939X_VNEG_CTRL_4_ILIM_SEL GENMASK(7, 4) +#define WCD939X_VNEG_CTRL_4_PW_BUF_POS GENMASK(3, 2) +#define WCD939X_VNEG_CTRL_4_PW_BUF_NEG GENMASK(1, 0) +#define WCD939X_FLYBACK_VNEG_CTRL_5 (0x30a9) +#define WCD939X_FLYBACK_VNEG_CTRL_6 (0x30aa) +#define WCD939X_FLYBACK_VNEG_CTRL_7 (0x30ab) +#define WCD939X_FLYBACK_VNEG_CTRL_8 (0x30ac) +#define WCD939X_FLYBACK_VNEG_CTRL_9 (0x30ad) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_1 (0x30ae) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_2 (0x30af) +#define WCD939X_FLYBACK_VNEGDAC_CTRL_3 (0x30b0) +#define WCD939X_FLYBACK_CTRL_1 (0x30b1) +#define WCD939X_FLYBACK_TEST_CTL (0x30b2) +#define WCD939X_RX_AUX_SW_CTL (0x30b3) +#define WCD939X_RX_PA_AUX_IN_CONN (0x30b4) +#define WCD939X_RX_TIMER_DIV (0x30b5) +#define WCD939X_RX_OCP_CTL (0x30b6) +#define WCD939X_RX_OCP_COUNT (0x30b7) +#define WCD939X_RX_BIAS_EAR_DAC (0x30b8) +#define WCD939X_RX_BIAS_EAR_AMP (0x30b9) +#define WCD939X_RX_BIAS_HPH_LDO (0x30ba) +#define WCD939X_RX_BIAS_HPH_PA (0x30bb) +#define WCD939X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30bc) +#define WCD939X_RX_BIAS_HPH_RDAC_LDO (0x30bd) +#define WCD939X_RX_BIAS_HPH_CNP1 (0x30be) +#define WCD939X_RX_BIAS_HPH_LOWPOWER (0x30bf) +#define WCD939X_RX_BIAS_AUX_DAC (0x30c0) +#define WCD939X_RX_BIAS_AUX_AMP (0x30c1) +#define WCD939X_RX_BIAS_VNEGDAC_BLEEDER (0x30c2) +#define WCD939X_RX_BIAS_MISC (0x30c3) +#define WCD939X_RX_BIAS_BUCK_RST (0x30c4) +#define WCD939X_RX_BIAS_BUCK_VREF_ERRAMP (0x30c5) +#define WCD939X_RX_BIAS_FLYB_ERRAMP (0x30c6) +#define WCD939X_RX_BIAS_FLYB_BUFF (0x30c7) +#define WCD939X_RX_BIAS_FLYB_MID_RST (0x30c8) +#define WCD939X_HPH_L_STATUS (0x30c9) +#define WCD939X_HPH_R_STATUS (0x30ca) +#define WCD939X_HPH_CNP_EN (0x30cb) +#define WCD939X_HPH_CNP_WG_CTL (0x30cc) +#define WCD939X_HPH_CNP_WG_TIME (0x30cd) +#define WCD939X_HPH_OCP_CTL (0x30ce) +#define WCD939X_OCP_CTL_OCP_CURR_LIMIT GENMASK(7, 5) +#define WCD939X_OCP_CTL_OCP_FSM_EN BIT(4) +#define WCD939X_OCP_CTL_SPARE_BITS BIT(3) +#define WCD939X_OCP_CTL_SCD_OP_EN BIT(1) +#define WCD939X_HPH_AUTO_CHOP (0x30cf) +#define WCD939X_HPH_CHOP_CTL (0x30d0) +#define WCD939X_HPH_PA_CTL1 (0x30d1) +#define WCD939X_HPH_PA_CTL2 (0x30d2) +#define WCD939X_PA_CTL2_HPHPA_GND_R BIT(6) +#define WCD939X_PA_CTL2_HPHPA_GND_L BIT(4) +#define WCD939X_PA_CTL2_GM3_CASCODE_CTL_NORMAL GENMASK(1, 0) +#define WCD939X_HPH_L_EN (0x30d3) +#define WCD939X_L_EN_CONST_SEL_L GENMASK(7, 6) +#define WCD939X_L_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_L_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_L_TEST (0x30d4) +#define WCD939X_HPH_L_ATEST (0x30d5) +#define WCD939X_HPH_R_EN (0x30d6) +#define WCD939X_R_EN_CONST_SEL_R GENMASK(7, 6) +#define WCD939X_R_EN_GAIN_SOURCE_SEL BIT(5) +#define WCD939X_R_EN_SPARE_BITS GENMASK(4, 0) +#define WCD939X_HPH_R_TEST (0x30d7) +#define WCD939X_HPH_R_ATEST (0x30d8) +#define WCD939X_R_ATEST_DACR_REF_ATEST1_CONN BIT(7) +#define WCD939X_R_ATEST_LDO1_R_ATEST2_CONN BIT(6) +#define WCD939X_R_ATEST_LDO_R_ATEST2_CAL BIT(5) +#define WCD939X_R_ATEST_LDO2_R_ATEST2_CONN BIT(4) +#define WCD939X_R_ATEST_LDO_1P65V_ATEST1_CONN BIT(3) +#define WCD939X_R_ATEST_HPH_GND_OVR BIT(1) +#define WCD939X_HPH_RDAC_CLK_CTL1 (0x30d9) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN BIT(7) +#define WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_DIV_CTRL GENMASK(6, 4) +#define WCD939X_RDAC_CLK_CTL1_SPARE_BITS GENMASK(3, 0) +#define WCD939X_HPH_RDAC_CLK_CTL2 (0x30da) +#define WCD939X_HPH_RDAC_LDO_CTL (0x30db) +#define WCD939X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30dc) +#define WCD939X_HPH_REFBUFF_UHQA_CTL (0x30dd) +#define WCD939X_REFBUFF_UHQA_CTL_SPARE_BITS GENMASK(7, 6) +#define WCD939X_REFBUFF_UHQA_CTL_HPH_VNEGREG2_COMP_CTL_OV BIT(5) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_RBIAS_ADJUST BIT(4) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFP_IOUT_CTL GENMASK(3, 2) +#define WCD939X_REFBUFF_UHQA_CTL_REFBUFN_IOUT_CTL GENMASK(1, 0) +#define WCD939X_HPH_REFBUFF_LP_CTL (0x30de) +#define WCD939X_REFBUFF_LP_CTL_HPH_VNEGREG2_CURR_COMP GENMASK(7, 6) +#define WCD939X_REFBUFF_LP_CTL_SPARE_BITS GENMASK(5, 4) +#define WCD939X_REFBUFF_LP_CTL_EN_PREREF_FILT_STARTUP_CLKDIV BIT(3) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_STARTUP_CLKDIV_CTL GENMASK(2, 1) +#define WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS BIT(0) +#define WCD939X_HPH_L_DAC_CTL (0x30df) +#define WCD939X_HPH_R_DAC_CTL (0x30e0) +#define WCD939X_HPH_SURGE_COMP_SEL (0x30e1) +#define WCD939X_HPH_SURGE_EN (0x30e2) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHL BIT(7) +#define WCD939X_EN_EN_SURGE_PROTECTION_HPHR BIT(6) +#define WCD939X_EN_SEL_SURGE_COMP_IQ GENMASK(5, 4) +#define WCD939X_EN_SURGE_VOLT_MODE_SHUTOFF_EN BIT(3) +#define WCD939X_EN_LATCH_INTR_OP_STG_HIZ_EN BIT(2) +#define WCD939X_EN_SURGE_LATCH_REG_RESET BIT(1) +#define WCD939X_EN_SWTICH_VN_VNDAC_NSURGE_EN BIT(0) +#define WCD939X_HPH_SURGE_MISC1 (0x30e3) +#define WCD939X_HPH_SURGE_STATUS (0x30e4) +#define WCD939X_EAR_EN (0x30e9) +#define WCD939X_EAR_PA_CON (0x30ea) +#define WCD939X_EAR_SP_CON (0x30eb) +#define WCD939X_EAR_DAC_CON (0x30ec) +#define WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL BIT(7) +#define WCD939X_DAC_CON_REF_DBG_EN BIT(6) +#define WCD939X_DAC_CON_REF_DBG_GAIN GENMASK(5, 3) +#define WCD939X_DAC_CON_GAIN_DAC GENMASK(2, 1) +#define WCD939X_DAC_CON_INV_DATA BIT(0) +#define WCD939X_EAR_CNP_FSM_CON (0x30ed) +#define WCD939X_EAR_TEST_CTL (0x30ee) +#define WCD939X_EAR_STATUS_REG_1 (0x30ef) +#define WCD939X_EAR_STATUS_REG_2 (0x30f0) +#define WCD939X_FLYBACK_NEW_CTRL_2 (0x30f6) +#define WCD939X_FLYBACK_NEW_CTRL_3 (0x30f7) +#define WCD939X_FLYBACK_NEW_CTRL_4 (0x30f8) +#define WCD939X_ANA_NEW_PAGE (0x3100) +#define WCD939X_HPH_NEW_ANA_HPH2 (0x3101) +#define WCD939X_HPH_NEW_ANA_HPH3 (0x3102) +#define WCD939X_SLEEP_CTL (0x3103) +#define WCD939X_SLEEP_WATCHDOG_CTL (0x3104) +#define WCD939X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311f) +#define WCD939X_MBHC_NEW_CTL_1 (0x3120) +#define WCD939X_CTL_1_RCO_EN BIT(7) +#define WCD939X_CTL_1_ADC_MODE BIT(4) +#define WCD939X_CTL_1_ADC_ENABLE BIT(3) +#define WCD939X_CTL_1_DETECTION_DONE BIT(2) +#define WCD939X_CTL_1_BTN_DBNC_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_CTL_2 (0x3121) +#define WCD939X_CTL_2_MUX_CTL GENMASK(6, 4) +#define WCD939X_CTL_2_M_RTH_CTL GENMASK(3, 2) +#define WCD939X_CTL_2_HS_VREF_CTL GENMASK(1, 0) +#define WCD939X_MBHC_NEW_PLUG_DETECT_CTL (0x3122) +#define WCD939X_MBHC_NEW_ZDET_ANA_CTL (0x3123) +#define WCD939X_ZDET_ANA_CTL_AVERAGING_EN BIT(7) +#define WCD939X_ZDET_ANA_CTL_MAXV_CTL GENMASK(6, 4) +#define WCD939X_ZDET_ANA_CTL_RANGE_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_ZDET_RAMP_CTL (0x3124) +#define WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL GENMASK(6, 4) +#define WCD939X_ZDET_RAMP_CTL_TIME_CTL GENMASK(3, 0) +#define WCD939X_MBHC_NEW_FSM_STATUS (0x3125) +#define WCD939X_FSM_STATUS_ADC_TIMEOUT BIT(7) +#define WCD939X_FSM_STATUS_ADC_COMPLETE BIT(6) +#define WCD939X_FSM_STATUS_HS_M_COMP_STATUS BIT(5) +#define WCD939X_FSM_STATUS_FAST_PRESS_FLAG_STATUS BIT(4) +#define WCD939X_FSM_STATUS_FAST_REMOVAL_FLAG_STATUS BIT(3) +#define WCD939X_FSM_STATUS_REMOVAL_FLAG_STATUS BIT(2) +#define WCD939X_FSM_STATUS_ELECT_REM_RT_STATUS BIT(1) +#define WCD939X_FSM_STATUS_BTN_STATUS BIT(0) +#define WCD939X_MBHC_NEW_ADC_RESULT (0x3126) +#define WCD939X_ADC_RESULT_VALUE GENMASK(7, 0) +#define WCD939X_TX_NEW_CH12_MUX (0x3127) +#define WCD939X_TX_NEW_CH34_MUX (0x3128) +#define WCD939X_DIE_CRACK_DET_EN (0x312c) +#define WCD939X_DIE_CRACK_DET_OUT (0x312d) +#define WCD939X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_L (0x3133) +#define WCD939X_PA_GAIN_CTL_L_EN_HPHPA_2VPK BIT(7) +#define WCD939X_PA_GAIN_CTL_L_RX_SUPPLY_LEVEL BIT(6) +#define WCD939X_PA_GAIN_CTL_L_DAC_DR_BOOST BIT(5) +#define WCD939X_PA_GAIN_CTL_L_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134) +#define WCD939X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135) +#define WCD939X_HPH_NEW_INT_PA_GAIN_CTL_R (0x3136) +#define WCD939X_PA_GAIN_CTL_R_D_RCO_CLK_EN BIT(7) +#define WCD939X_PA_GAIN_CTL_R_SPARE_BITS GENMASK(6, 5) +#define WCD939X_PA_GAIN_CTL_R_VALUE GENMASK(4, 0) +#define WCD939X_HPH_NEW_INT_PA_MISC1 (0x3137) +#define WCD939X_HPH_NEW_INT_PA_MISC2 (0x3138) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC (0x3139) +#define WCD939X_HPH_NEW_INT_TIMER1 (0x313a) +#define WCD939X_TIMER1_CURR_IDIV_CTL_CMPDR_OFF GENMASK(7, 5) +#define WCD939X_TIMER1_CURR_IDIV_CTL_AUTOCHOP GENMASK(4, 2) +#define WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN BIT(1) +#define WCD939X_HPH_NEW_INT_TIMER2 (0x313b) +#define WCD939X_HPH_NEW_INT_TIMER3 (0x313c) +#define WCD939X_HPH_NEW_INT_TIMER4 (0x313d) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313e) +#define WCD939X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313f) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3140) +#define WCD939X_RDAC_HD2_CTL_L_EN_HD2_RES_DIV_L BIT(7) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L GENMASK(5, 0) +#define WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3141) +#define WCD939X_RDAC_HD2_CTL_R_EN_HD2_RES_DIV_R BIT(7) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_PULLGND_L BIT(6) +#define WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R GENMASK(5, 0) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145) +#define WCD939X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146) +#define WCD939X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31af) +#define WCD939X_MOISTURE_DET_DC_CTRL_ONCOUNT GENMASK(6, 5) +#define WCD939X_MOISTURE_DET_DC_CTRL_OFFCOUNT GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31b0) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_HPHL_PA_EN BIT(6) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_DTEST_EN GENMASK(5, 4) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_OVRD_POLLING BIT(3) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING BIT(2) +#define WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_DBNC_TIME GENMASK(1, 0) +#define WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31b1) +#define WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL GENMASK(4, 0) +#define WCD939X_MBHC_NEW_INT_ZDET_CLK_AND_MOISTURE_CTL_NEW (0x31b2) +#define WCD939X_EAR_INT_NEW_CHOPPER_CON (0x31b7) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON1 (0x31b8) +#define WCD939X_EAR_INT_NEW_CNP_VCM_CON2 (0x31b9) +#define WCD939X_EAR_INT_NEW_DYNAMIC_BIAS (0x31ba) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_1 (0x31d0) +#define WCD939X_SLEEP_INT_WATCHDOG_CTL_2 (0x31d1) +#define WCD939X_DIE_CRACK_INT_DET_INT1 (0x31d3) +#define WCD939X_DIE_CRACK_INT_DET_INT2 (0x31d4) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L2 (0x31d5) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L1 (0x31d6) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_L0 (0x31d7) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP1P2M (0x31d8) +#define WCD939X_TX_COM_NEW_INT_FE_DIVSTOP_ULP0P6M (0x31d9) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L2L1 (0x31da) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_L0 (0x31db) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG1_ULP (0x31dc) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L2L1 (0x31dd) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_L0 (0x31de) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP (0x31df) +#define WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE GENMASK(4, 0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_L2L1L0 (0x31e0) +#define WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP (0x31e1) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M GENMASK(7, 4) +#define WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE GENMASK(3, 0) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L2L1 (0x31e2) +#define WCD939X_TX_COM_NEW_INT_ADC_SCBIAS_L0ULP (0x31e3) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L2 (0x31e4) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L1 (0x31e5) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_L0 (0x31e6) +#define WCD939X_TX_COM_NEW_INT_ADC_INT_ULP (0x31e7) +#define WCD939X_DIGITAL_PAGE (0x3400) +#define WCD939X_DIGITAL_CHIP_ID0 (0x3401) +#define WCD939X_DIGITAL_CHIP_ID1 (0x3402) +#define WCD939X_DIGITAL_CHIP_ID2 (0x3403) +#define WCD939X_DIGITAL_CHIP_ID3 (0x3404) +#define WCD939X_DIGITAL_SWR_TX_CLK_RATE (0x3405) +#define WCD939X_DIGITAL_CDC_RST_CTL (0x3406) +#define WCD939X_DIGITAL_TOP_CLK_CFG (0x3407) +#define WCD939X_DIGITAL_CDC_ANA_CLK_CTL (0x3408) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV4_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN BIT(0) +#define WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN BIT(4) +#define WCD939X_DIGITAL_CDC_DIG_CLK_CTL (0x3409) +#define WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN BIT(7) +#define WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN BIT(6) +#define WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN BIT(5) +#define WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN BIT(4) +#define WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN BIT(2) +#define WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN BIT(1) +#define WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN BIT(0) +#define WCD939X_DIGITAL_SWR_RST_EN (0x340a) +#define WCD939X_DIGITAL_CDC_PATH_MODE (0x340b) +#define WCD939X_DIGITAL_CDC_RX_RST (0x340c) +#define WCD939X_DIGITAL_CDC_RX0_CTL (0x340d) +#define WCD939X_DIGITAL_CDC_RX1_CTL (0x340e) +#define WCD939X_DIGITAL_CDC_RX2_CTL (0x340f) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE GENMASK(7, 4) +#define WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_COMP_CTL_0 (0x3414) +#define WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN BIT(1) +#define WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN BIT(0) +#define WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_MBHC_1P2M_CLK_EN BIT(5) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX3_ADC_CLK_EN BIT(4) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX2_ADC_CLK_EN BIT(3) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX1_ADC_CLK_EN BIT(2) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TX0_ADC_CLK_EN BIT(1) +#define WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN BIT(0) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341e) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341f) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422) +#define WCD939X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426) +#define WCD939X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R1 (0x3428) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R2 (0x3429) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R3 (0x342a) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R4 (0x342b) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R5 (0x342c) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R6 (0x342d) +#define WCD939X_DIGITAL_CDC_HPH_DSM_R7 (0x342e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_0 (0x342f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A1_1 (0x3430) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_0 (0x3431) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A2_1 (0x3432) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_0 (0x3433) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A3_1 (0x3434) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_0 (0x3435) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A4_1 (0x3436) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_0 (0x3437) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A5_1 (0x3438) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A6_0 (0x3439) +#define WCD939X_DIGITAL_CDC_EAR_DSM_A7_0 (0x343a) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_0 (0x343b) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_1 (0x343c) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_2 (0x343d) +#define WCD939X_DIGITAL_CDC_EAR_DSM_C_3 (0x343e) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R1 (0x343f) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R2 (0x3440) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R3 (0x3441) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R4 (0x3442) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R5 (0x3443) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R6 (0x3444) +#define WCD939X_DIGITAL_CDC_EAR_DSM_R7 (0x3445) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344a) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_0 (0x344b) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_1 (0x344c) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_DSD_2 (0x344d) +#define WCD939X_DIGITAL_CDC_HPH_GAIN_CTL (0x344e) +#define WCD939X_CDC_HPH_GAIN_CTL_HPH_STEREO_EN BIT(4) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN BIT(3) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN BIT(2) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHR_DSD_EN BIT(1) +#define WCD939X_CDC_HPH_GAIN_CTL_HPHL_DSD_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_GAIN_CTL (0x344f) +#define WCD939X_CDC_EAR_GAIN_CTL_EAR_EN BIT(0) +#define WCD939X_DIGITAL_CDC_EAR_PATH_CTL (0x3450) +#define WCD939X_DIGITAL_CDC_SWR_CLH (0x3451) +#define WCD939X_CDC_SWR_CLH_CLH_CTL GENMASK(7, 0) +#define WCD939X_DIGITAL_SWR_CLH_BYP (0x3452) +#define WCD939X_DIGITAL_CDC_TX0_CTL (0x3453) +#define WCD939X_DIGITAL_CDC_TX1_CTL (0x3454) +#define WCD939X_DIGITAL_CDC_TX2_CTL (0x3455) +#define WCD939X_DIGITAL_CDC_TX_RST (0x3456) +#define WCD939X_DIGITAL_CDC_REQ_CTL (0x3457) +#define WCD939X_CDC_REQ_CTL_TX3_WIDE_BAND BIT(5) +#define WCD939X_CDC_REQ_CTL_TX2_WIDE_BAND BIT(4) +#define WCD939X_CDC_REQ_CTL_TX1_WIDE_BAND BIT(3) +#define WCD939X_CDC_REQ_CTL_TX0_WIDE_BAND BIT(2) +#define WCD939X_CDC_REQ_CTL_FS_RATE_4P8 BIT(1) +#define WCD939X_CDC_REQ_CTL_NO_NOTCH BIT(0) +#define WCD939X_DIGITAL_CDC_RST (0x3458) +#define WCD939X_DIGITAL_CDC_AMIC_CTL (0x345a) +#define WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL BIT(3) +#define WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL BIT(2) +#define WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL BIT(1) +#define WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC_CTL (0x345b) +#define WCD939X_CDC_DMIC_CTL_DMIC_LEGACY_SW_MODE BIT(3) +#define WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN BIT(2) +#define WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN BIT(1) +#define WCD939X_CDC_DMIC_CTL_SOFT_RESET BIT(0) +#define WCD939X_DIGITAL_CDC_DMIC1_CTL (0x345c) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC1_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC2_CTL (0x345d) +#define WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN BIT(7) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC2_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC3_CTL (0x345e) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC3_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_CDC_DMIC4_CTL (0x345f) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SCALE_SEL GENMASK(6, 4) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN BIT(3) +#define WCD939X_CDC_DMIC4_CTL_DMIC_CLK_SEL GENMASK(2, 0) +#define WCD939X_DIGITAL_EFUSE_PRG_CTL (0x3460) +#define WCD939X_DIGITAL_EFUSE_CTL (0x3461) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE GENMASK(7, 4) +#define WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE GENMASK(3, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL0 (0x3465) +#define WCD939X_PDM_WD_CTL0_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL0_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL0_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL1 (0x3466) +#define WCD939X_PDM_WD_CTL1_HOLD_OFF BIT(4) +#define WCD939X_PDM_WD_CTL1_TIME_OUT_SEL BIT(3) +#define WCD939X_PDM_WD_CTL1_PDM_WD_EN GENMASK(2, 0) +#define WCD939X_DIGITAL_PDM_WD_CTL2 (0x3467) +#define WCD939X_DIGITAL_INTR_MODE (0x346a) +#define WCD939X_DIGITAL_INTR_MASK_0 (0x346b) +#define WCD939X_DIGITAL_INTR_MASK_1 (0x346c) +#define WCD939X_DIGITAL_INTR_MASK_2 (0x346d) +#define WCD939X_DIGITAL_INTR_STATUS_0 (0x346e) +#define WCD939X_DIGITAL_INTR_STATUS_1 (0x346f) +#define WCD939X_DIGITAL_INTR_STATUS_2 (0x3470) +#define WCD939X_DIGITAL_INTR_CLEAR_0 (0x3471) +#define WCD939X_DIGITAL_INTR_CLEAR_1 (0x3472) +#define WCD939X_DIGITAL_INTR_CLEAR_2 (0x3473) +#define WCD939X_DIGITAL_INTR_LEVEL_0 (0x3474) +#define WCD939X_DIGITAL_INTR_LEVEL_1 (0x3475) +#define WCD939X_DIGITAL_INTR_LEVEL_2 (0x3476) +#define WCD939X_DIGITAL_INTR_SET_0 (0x3477) +#define WCD939X_DIGITAL_INTR_SET_1 (0x3478) +#define WCD939X_DIGITAL_INTR_SET_2 (0x3479) +#define WCD939X_DIGITAL_INTR_TEST_0 (0x347a) +#define WCD939X_DIGITAL_INTR_TEST_1 (0x347b) +#define WCD939X_DIGITAL_INTR_TEST_2 (0x347c) +#define WCD939X_DIGITAL_TX_MODE_DBG_EN (0x347f) +#define WCD939X_DIGITAL_TX_MODE_DBG_0_1 (0x3480) +#define WCD939X_DIGITAL_TX_MODE_DBG_2_3 (0x3481) +#define WCD939X_DIGITAL_LB_IN_SEL_CTL (0x3482) +#define WCD939X_DIGITAL_LOOP_BACK_MODE (0x3483) +#define WCD939X_DIGITAL_SWR_DAC_TEST (0x3484) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486) +#define WCD939X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488) +#define WCD939X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489) +#define WCD939X_DIGITAL_SWR_HM_TEST_0 (0x348a) +#define WCD939X_DIGITAL_SWR_HM_TEST_1 (0x348b) +#define WCD939X_DIGITAL_PAD_CTL_SWR_0 (0x348c) +#define WCD939X_DIGITAL_PAD_CTL_SWR_1 (0x348d) +#define WCD939X_DIGITAL_I2C_CTL (0x348e) +#define WCD939X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348f) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490) +#define WCD939X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491) +#define WCD939X_DIGITAL_EFUSE_T_DATA_0 (0x3492) +#define WCD939X_DIGITAL_EFUSE_T_DATA_1 (0x3493) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494) +#define WCD939X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497) +#define WCD939X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498) +#define WCD939X_DIGITAL_PAD_INP_DIS_0 (0x3499) +#define WCD939X_DIGITAL_PAD_INP_DIS_1 (0x349a) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_0 (0x349b) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_1 (0x349c) +#define WCD939X_DIGITAL_DRIVE_STRENGTH_2 (0x349d) +#define WCD939X_DIGITAL_RX_DATA_EDGE_CTL (0x349e) +#define WCD939X_DIGITAL_TX_DATA_EDGE_CTL (0x349f) +#define WCD939X_DIGITAL_GPIO_MODE (0x34a0) +#define WCD939X_DIGITAL_PIN_CTL_OE (0x34a1) +#define WCD939X_DIGITAL_PIN_CTL_DATA_0 (0x34a2) +#define WCD939X_DIGITAL_PIN_CTL_DATA_1 (0x34a3) +#define WCD939X_DIGITAL_PIN_STATUS_0 (0x34a4) +#define WCD939X_DIGITAL_PIN_STATUS_1 (0x34a5) +#define WCD939X_DIGITAL_DIG_DEBUG_CTL (0x34a6) +#define WCD939X_DIGITAL_DIG_DEBUG_EN (0x34a7) +#define WCD939X_DIGITAL_ANA_CSR_DBG_ADD (0x34a8) +#define WCD939X_DIGITAL_ANA_CSR_DBG_CTL (0x34a9) +#define WCD939X_DIGITAL_SSP_DBG (0x34aa) +#define WCD939X_DIGITAL_MODE_STATUS_0 (0x34ab) +#define WCD939X_DIGITAL_MODE_STATUS_1 (0x34ac) +#define WCD939X_DIGITAL_SPARE_0 (0x34ad) +#define WCD939X_DIGITAL_SPARE_1 (0x34ae) +#define WCD939X_DIGITAL_SPARE_2 (0x34af) +#define WCD939X_DIGITAL_EFUSE_REG_0 (0x34b0) +#define WCD939X_EFUSE_REG_0_WCD939X_ID GENMASK(4, 1) +#define WCD939X_EFUSE_REG_0_EFUSE_BLOWN BIT(0) +#define WCD939X_DIGITAL_EFUSE_REG_1 (0x34b1) +#define WCD939X_DIGITAL_EFUSE_REG_2 (0x34b2) +#define WCD939X_DIGITAL_EFUSE_REG_3 (0x34b3) +#define WCD939X_DIGITAL_EFUSE_REG_4 (0x34b4) +#define WCD939X_DIGITAL_EFUSE_REG_5 (0x34b5) +#define WCD939X_DIGITAL_EFUSE_REG_6 (0x34b6) +#define WCD939X_DIGITAL_EFUSE_REG_7 (0x34b7) +#define WCD939X_DIGITAL_EFUSE_REG_8 (0x34b8) +#define WCD939X_DIGITAL_EFUSE_REG_9 (0x34b9) +#define WCD939X_DIGITAL_EFUSE_REG_10 (0x34ba) +#define WCD939X_DIGITAL_EFUSE_REG_11 (0x34bb) +#define WCD939X_DIGITAL_EFUSE_REG_12 (0x34bc) +#define WCD939X_DIGITAL_EFUSE_REG_13 (0x34bd) +#define WCD939X_DIGITAL_EFUSE_REG_14 (0x34be) +#define WCD939X_DIGITAL_EFUSE_REG_15 (0x34bf) +#define WCD939X_DIGITAL_EFUSE_REG_16 (0x34c0) +#define WCD939X_DIGITAL_EFUSE_REG_17 (0x34c1) +#define WCD939X_DIGITAL_EFUSE_REG_18 (0x34c2) +#define WCD939X_DIGITAL_EFUSE_REG_19 (0x34c3) +#define WCD939X_DIGITAL_EFUSE_REG_20 (0x34c4) +#define WCD939X_DIGITAL_EFUSE_REG_21 (0x34c5) +#define WCD939X_DIGITAL_EFUSE_REG_22 (0x34c6) +#define WCD939X_DIGITAL_EFUSE_REG_23 (0x34c7) +#define WCD939X_DIGITAL_EFUSE_REG_24 (0x34c8) +#define WCD939X_DIGITAL_EFUSE_REG_25 (0x34c9) +#define WCD939X_DIGITAL_EFUSE_REG_26 (0x34ca) +#define WCD939X_DIGITAL_EFUSE_REG_27 (0x34cb) +#define WCD939X_DIGITAL_EFUSE_REG_28 (0x34cc) +#define WCD939X_DIGITAL_EFUSE_REG_29 (0x34cd) +#define WCD939X_DIGITAL_EFUSE_REG_30 (0x34ce) +#define WCD939X_DIGITAL_EFUSE_REG_31 (0x34cf) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_0 (0x34d0) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_1 (0x34d1) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_2 (0x34d2) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_3 (0x34d3) +#define WCD939X_DIGITAL_TX_REQ_FB_CTL_4 (0x34d4) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA0 (0x34d5) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA1 (0x34d6) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA2 (0x34d7) +#define WCD939X_DIGITAL_DEM_BYPASS_DATA3 (0x34d8) +#define WCD939X_DIGITAL_DEM_SECOND_ORDER (0x34d9) +#define WCD939X_DIGITAL_DSM_CTRL (0x34da) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_0 (0x34db) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_1 (0x34dc) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_2 (0x34dd) +#define WCD939X_DIGITAL_DSM_0_STATIC_DATA_3 (0x34de) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_0 (0x34df) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_1 (0x34e0) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_2 (0x34e1) +#define WCD939X_DIGITAL_DSM_1_STATIC_DATA_3 (0x34e2) +#define WCD939X_RX_TOP_PAGE (0x3500) +#define WCD939X_RX_TOP_TOP_CFG0 (0x3501) +#define WCD939X_TOP_CFG0_HPH_DAC_RATE_SEL BIT(1) +#define WCD939X_TOP_CFG0_PGA_UPDATE BIT(0) +#define WCD939X_RX_TOP_HPHL_COMP_WR_LSB (0x3502) +#define WCD939X_RX_TOP_HPHL_COMP_WR_MSB (0x3503) +#define WCD939X_RX_TOP_HPHL_COMP_LUT (0x3504) +#define WCD939X_RX_TOP_HPHL_COMP_RD_LSB (0x3505) +#define WCD939X_RX_TOP_HPHL_COMP_RD_MSB (0x3506) +#define WCD939X_RX_TOP_HPHR_COMP_WR_LSB (0x3507) +#define WCD939X_RX_TOP_HPHR_COMP_WR_MSB (0x3508) +#define WCD939X_RX_TOP_HPHR_COMP_LUT (0x3509) +#define WCD939X_RX_TOP_HPHR_COMP_RD_LSB (0x350a) +#define WCD939X_RX_TOP_HPHR_COMP_RD_MSB (0x350b) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG1 (0x350c) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG2 (0x350d) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG3 (0x350e) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG4 (0x350f) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG5 (0x3510) +#define WCD939X_RX_TOP_DSD0_DEBUG_CFG6 (0x3511) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG1 (0x3512) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG2 (0x3513) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG3 (0x3514) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG4 (0x3515) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG5 (0x3516) +#define WCD939X_RX_TOP_DSD1_DEBUG_CFG6 (0x3517) +#define WCD939X_RX_TOP_HPHL_PATH_CFG0 (0x351c) +#define WCD939X_HPHL_PATH_CFG0_INT_EN BIT(1) +#define WCD939X_HPHL_PATH_CFG0_DLY_ZN_EN BIT(0) +#define WCD939X_RX_TOP_HPHL_PATH_CFG1 (0x351d) +#define WCD939X_HPHL_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHL_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHL_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHL_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHL_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_HPHR_PATH_CFG0 (0x351e) +#define WCD939X_HPHR_PATH_CFG0_INT_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG0_DLY_ZN_EN BIT(1) +#define WCD939X_RX_TOP_HPHR_PATH_CFG1 (0x351f) +#define WCD939X_HPHR_PATH_CFG1_DSM_SOFT_RST BIT(5) +#define WCD939X_HPHR_PATH_CFG1_INT_SOFT_RST BIT(4) +#define WCD939X_HPHR_PATH_CFG1_FMT_CONV BIT(3) +#define WCD939X_HPHR_PATH_CFG1_IDLE_OVRD_EN BIT(2) +#define WCD939X_HPHR_PATH_CFG1_RX_DC_DROOP_COEFF_SEL GENMASK(1, 0) +#define WCD939X_RX_TOP_PATH_CFG2 (0x3520) +#define WCD939X_RX_TOP_HPHL_PATH_SEC0 (0x3521) +#define WCD939X_RX_TOP_HPHL_PATH_SEC1 (0x3522) +#define WCD939X_RX_TOP_HPHL_PATH_SEC2 (0x3523) +#define WCD939X_RX_TOP_HPHL_PATH_SEC3 (0x3524) +#define WCD939X_RX_TOP_HPHR_PATH_SEC0 (0x3525) +#define WCD939X_RX_TOP_HPHR_PATH_SEC1 (0x3526) +#define WCD939X_RX_TOP_HPHR_PATH_SEC2 (0x3527) +#define WCD939X_RX_TOP_HPHR_PATH_SEC3 (0x3528) +#define WCD939X_RX_TOP_PATH_SEC4 (0x3529) +#define WCD939X_RX_TOP_PATH_SEC5 (0x352a) +#define WCD939X_COMPANDER_HPHL_CTL0 (0x3540) +#define WCD939X_COMPANDER_HPHL_CTL1 (0x3541) +#define WCD939X_COMPANDER_HPHL_CTL2 (0x3542) +#define WCD939X_COMPANDER_HPHL_CTL3 (0x3543) +#define WCD939X_COMPANDER_HPHL_CTL4 (0x3544) +#define WCD939X_COMPANDER_HPHL_CTL5 (0x3545) +#define WCD939X_COMPANDER_HPHL_CTL6 (0x3546) +#define WCD939X_COMPANDER_HPHL_CTL7 (0x3547) +#define WCD939X_COMPANDER_HPHL_CTL8 (0x3548) +#define WCD939X_COMPANDER_HPHL_CTL9 (0x3549) +#define WCD939X_COMPANDER_HPHL_CTL10 (0x354a) +#define WCD939X_COMPANDER_HPHL_CTL11 (0x354b) +#define WCD939X_COMPANDER_HPHL_CTL12 (0x354c) +#define WCD939X_COMPANDER_HPHL_CTL13 (0x354d) +#define WCD939X_COMPANDER_HPHL_CTL14 (0x354e) +#define WCD939X_COMPANDER_HPHL_CTL15 (0x354f) +#define WCD939X_COMPANDER_HPHL_CTL16 (0x3550) +#define WCD939X_COMPANDER_HPHL_CTL17 (0x3551) +#define WCD939X_COMPANDER_HPHL_CTL18 (0x3552) +#define WCD939X_COMPANDER_HPHL_CTL19 (0x3553) +#define WCD939X_R_CTL0 (0x3560) +#define WCD939X_R_CTL1 (0x3561) +#define WCD939X_R_CTL2 (0x3562) +#define WCD939X_R_CTL3 (0x3563) +#define WCD939X_R_CTL4 (0x3564) +#define WCD939X_R_CTL5 (0x3565) +#define WCD939X_R_CTL6 (0x3566) +#define WCD939X_R_CTL7 (0x3567) +#define WCD939X_R_CTL8 (0x3568) +#define WCD939X_R_CTL9 (0x3569) +#define WCD939X_R_CTL10 (0x356a) +#define WCD939X_R_CTL11 (0x356b) +#define WCD939X_R_CTL12 (0x356c) +#define WCD939X_R_CTL13 (0x356d) +#define WCD939X_R_CTL14 (0x356e) +#define WCD939X_R_CTL15 (0x356f) +#define WCD939X_R_CTL16 (0x3570) +#define WCD939X_R_CTL17 (0x3571) +#define WCD939X_R_CTL18 (0x3572) +#define WCD939X_R_CTL19 (0x3573) +#define WCD939X_E_PATH_CTL (0x3580) +#define WCD939X_E_CFG0 (0x3581) +#define WCD939X_CFG0_AUTO_DISABLE_ANC BIT(2) +#define WCD939X_CFG0_AUTO_DISABLE_DSD BIT(1) +#define WCD939X_CFG0_IDLE_STEREO BIT(0) +#define WCD939X_E_CFG1 (0x3582) +#define WCD939X_E_CFG2 (0x3583) +#define WCD939X_E_CFG3 (0x3584) +#define WCD939X_DSD_HPHL_PATH_CTL (0x3590) +#define WCD939X_DSD_HPHL_CFG0 (0x3591) +#define WCD939X_DSD_HPHL_CFG1 (0x3592) +#define WCD939X_DSD_HPHL_CFG2 (0x3593) +#define WCD939X_DSD_HPHL_CFG3 (0x3594) +#define WCD939X_DSD_HPHL_CFG4 (0x3595) +#define WCD939X_DSD_HPHL_CFG5 (0x3596) +#define WCD939X_DSD_HPHR_PATH_CTL (0x35a0) +#define WCD939X_DSD_HPHR_CFG0 (0x35a1) +#define WCD939X_DSD_HPHR_CFG1 (0x35a2) +#define WCD939X_DSD_HPHR_CFG2 (0x35a3) +#define WCD939X_DSD_HPHR_CFG3 (0x35a4) +#define WCD939X_DSD_HPHR_CFG4 (0x35a5) +#define WCD939X_DSD_HPHR_CFG5 (0x35a6) +#define WCD939X_MAX_REGISTER (WCD939X_DSD_HPHR_CFG5) + +#define WCD939X_MAX_SWR_PORTS (6) +#define WCD939X_MAX_RX_SWR_PORTS (6) +#define WCD939X_MAX_TX_SWR_PORTS (4) +#define WCD939X_MAX_SWR_CH_IDS (15) + +struct wcd939x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +enum wcd939x_tx_sdw_ports { + WCD939X_ADC_1_4_PORT = 1, + WCD939X_ADC_DMIC_1_2_PORT, + WCD939X_DMIC_0_3_MBHC_PORT, + WCD939X_DMIC_3_7_PORT, +}; + +enum wcd939x_tx_sdw_channels { + WCD939X_ADC1, + WCD939X_ADC2, + WCD939X_ADC3, + WCD939X_ADC4, + WCD939X_DMIC0, + WCD939X_DMIC1, + WCD939X_MBHC, + WCD939X_DMIC2, + WCD939X_DMIC3, + WCD939X_DMIC4, + WCD939X_DMIC5, + WCD939X_DMIC6, + WCD939X_DMIC7, +}; + +enum wcd939x_rx_sdw_ports { + WCD939X_HPH_PORT = 1, + WCD939X_CLSH_PORT, + WCD939X_COMP_PORT, + WCD939X_LO_PORT, + WCD939X_DSD_PORT, + WCD939X_HIFI_PCM_PORT, +}; + +enum wcd939x_rx_sdw_channels { + WCD939X_HPH_L, + WCD939X_HPH_R, + WCD939X_CLSH, + WCD939X_COMP_L, + WCD939X_COMP_R, + WCD939X_LO, + WCD939X_DSD_L, + WCD939X_DSD_R, + WCD939X_HIFI_PCM_L, + WCD939X_HIFI_PCM_R, +}; + +enum { + WCD939X_SDW_DIR_RX, + WCD939X_SDW_DIR_TX, +}; + +struct wcd939x_priv; +struct wcd939x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; + struct wcd939x_sdw_ch_info *ch_info; + bool port_enable[WCD939X_MAX_SWR_CH_IDS]; + int active_ports; + int num_ports; + bool is_tx; + struct wcd939x_priv *wcd939x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD939X_SDW) +int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd939x_sdw_device_get(struct device_node *np); +unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev); + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd); +#else + +static inline int wcd939x_sdw_free(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_set_sdw_stream(struct wcd939x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +static inline int wcd939x_sdw_hw_params(struct wcd939x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline struct device *wcd939x_sdw_device_get(struct device_node *np) +{ + return NULL; +} + +static inline unsigned int wcd939x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return 0; +} + +struct regmap *wcd939x_swr_get_regmap(struct wcd939x_sdw_priv *wcd) +{ + return PTR_ERR(-EINVAL); +} +#endif /* CONFIG_SND_SOC_WCD939X_SDW */ + +#endif /* __WCD939X_H__ */ -- cgit v1.2.3 From 10f514bd172a40b9d03d759678e4711612d671a1 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 19 Dec 2023 13:45:38 +0100 Subject: ASoC: codecs: Add WCD939x Codec driver Add the main WCD9390/WCD9395 Audio Codec driver to support: - 4 ADC inputs for up to 5 Analog Microphones - 4 DMIC inputs for up to 8 Digital Microphones - 4 Microphone BIAS - Stereo Headphone output - Mono EAR output - MBHC engine for Headset Detection It makes usage of the generic MBHC and CLSH generic code and the USB Type-C mux and switch helpers to gather USB-C Events in order to properly setup Headset Detection mechanism when connected behind the separate USB-C Mux subsystem. WCD9390/WCD9395 supports a PCM path for Playback instead of the actually implemented PDM playback, it will be implemented later. Signed-off-by: Neil Armstrong Link: https://msgid.link/r/20231219-topic-sm8650-upstream-wcd939x-codec-v4-5-1c3bbff2d7ab@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 6 + sound/soc/codecs/wcd-clsh-v2.h | 1 + sound/soc/codecs/wcd939x.c | 3686 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 3702 insertions(+) create mode 100644 sound/soc/codecs/wcd939x.c (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 78552a497eaa..1b21d2cc44d7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2060,8 +2060,17 @@ config SND_SOC_WCD938X_SDW The WCD9380/9385 is a audio codec IC Integrated in Qualcomm SoCs like SM8250. +config SND_SOC_WCD939X + depends on SND_SOC_WCD939X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + depends on TYPEC || !TYPEC + select SND_SOC_WCD_CLASSH + config SND_SOC_WCD939X_SDW tristate "WCD9390/WCD9395 Codec - SDW" + select SND_SOC_WCD939X + select SND_SOC_WCD_MBHC select REGMAP_IRQ depends on SOUNDWIRE select REGMAP_SOUNDWIRE diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 46f78d539278..8217f2868f4e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -313,6 +313,7 @@ snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd938x-objs := wcd938x.o snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o +snd-soc-wcd939x-objs := wcd939x.o snd-soc-wcd939x-sdw-objs := wcd939x-sdw.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o @@ -704,6 +705,11 @@ ifdef CONFIG_SND_SOC_WCD938X_SDW # avoid link failure by forcing sdw code built-in when needed obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o endif +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x.o +ifdef CONFIG_SND_SOC_WCD939X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o +endif obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h index 4e3653058275..eeb9bc5b01e2 100644 --- a/sound/soc/codecs/wcd-clsh-v2.h +++ b/sound/soc/codecs/wcd-clsh-v2.h @@ -47,6 +47,7 @@ enum wcd_codec_version { /* New CLSH after this */ WCD937X = 2, WCD938X = 3, + WCD939X = 4, }; struct wcd_clsh_ctrl; diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c new file mode 100644 index 000000000000..0ccc7b31d0c1 --- /dev/null +++ b/sound/soc/codecs/wcd939x.c @@ -0,0 +1,3686 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#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 + +enum { + WCD939X_VERSION_1_0 = 0, + WCD939X_VERSION_1_1, + WCD939X_VERSION_2_0, +}; + +#define WCD939X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD939X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) +#define WCD939X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) +#define SWR_CLK_RATE_0P6MHZ (600000) +#define SWR_CLK_RATE_1P2MHZ (1200000) +#define SWR_CLK_RATE_2P4MHZ (2400000) +#define SWR_CLK_RATE_4P8MHZ (4800000) +#define SWR_CLK_RATE_9P6MHZ (9600000) +#define SWR_CLK_RATE_11P2896MHZ (1128960) + +#define ADC_MODE_VAL_HIFI 0x01 +#define ADC_MODE_VAL_LO_HIF 0x02 +#define ADC_MODE_VAL_NORMAL 0x03 +#define ADC_MODE_VAL_LP 0x05 +#define ADC_MODE_VAL_ULP1 0x09 +#define ADC_MODE_VAL_ULP2 0x0B + +/* Z value defined in milliohm */ +#define WCD939X_ZDET_VAL_32 (32000) +#define WCD939X_ZDET_VAL_400 (400000) +#define WCD939X_ZDET_VAL_1200 (1200000) +#define WCD939X_ZDET_VAL_100K (100000000) + +/* Z floating defined in ohms */ +#define WCD939X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD939X_ZDET_NUM_MEASUREMENTS (900) +#define WCD939X_MBHC_GET_C1(c) (((c) & 0xC000) >> 14) +#define WCD939X_MBHC_GET_X1(x) ((x) & 0x3FFF) + +/* Z value compared in milliOhm */ +#define WCD939X_MBHC_IS_SECOND_RAMP_REQUIRED(z) false +#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, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD939X_IRQ_MBHC_SW_DET, + WCD939X_IRQ_HPHR_OCP_INT, + WCD939X_IRQ_HPHR_CNP_INT, + WCD939X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD939X_IRQ_HPHL_CNP_INT, + WCD939X_IRQ_EAR_CNP_INT, + WCD939X_IRQ_EAR_SCD_INT, + WCD939X_IRQ_HPHL_PDM_WD_INT, + WCD939X_IRQ_HPHR_PDM_WD_INT, + WCD939X_IRQ_EAR_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD939X_IRQ_MBHC_MOISTURE_INT, + WCD939X_IRQ_HPHL_SURGE_DET_INT, + WCD939X_IRQ_HPHR_SURGE_DET_INT, + WCD939X_NUM_IRQS, +}; + +enum { + MICB_BIAS_DISABLE = 0, + MICB_BIAS_ENABLE, + MICB_BIAS_PULL_UP, + MICB_BIAS_PULL_DOWN, +}; + +enum { + WCD_ADC1 = 0, + WCD_ADC2, + WCD_ADC3, + WCD_ADC4, + HPH_PA_DELAY, +}; + +enum { + ADC_MODE_INVALID = 0, + ADC_MODE_HIFI, + ADC_MODE_LO_HIF, + ADC_MODE_NORMAL, + ADC_MODE_LP, + ADC_MODE_ULP1, + ADC_MODE_ULP2, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +static u8 tx_mode_bit[] = { + [ADC_MODE_INVALID] = 0x00, + [ADC_MODE_HIFI] = 0x01, + [ADC_MODE_LO_HIF] = 0x02, + [ADC_MODE_NORMAL] = 0x04, + [ADC_MODE_LP] = 0x08, + [ADC_MODE_ULP1] = 0x10, + [ADC_MODE_ULP2] = 0x20, +}; + +struct zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +struct wcd939x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd939x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode, *txnode; + struct regmap *regmap; + struct snd_soc_component *component; + /* micb setup lock */ + struct mutex micb_lock; + /* typec handling */ + bool typec_analog_mux; +#if IS_ENABLED(CONFIG_TYPEC) + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_sw; + enum typec_orientation typec_orientation; + unsigned long typec_mode; + struct typec_switch *typec_switch; +#endif /* CONFIG_TYPEC */ + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + 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]; + u32 hph_mode; + u32 tx_mode[TX_ADC_MAX]; + int variant; + int reset_gpio; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int ear_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + bool ldoh; +}; + +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); + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD939X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD939X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD939X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD939X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD939X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD939X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD939X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD939X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD939X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD939X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD939X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD939X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD939X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD939X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD939X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD939X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD939X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD939X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD939X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD939X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD939X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD939X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD939X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD939X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD939X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD939X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD939X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD939X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD939X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD939X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD939X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD939X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD939X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD939X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_SCD_INT, 1, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(WCD939X_IRQ_EAR_PDM_WD_INT, 1, 0x80), + REGMAP_IRQ_REG(WCD939X_IRQ_MBHC_MOISTURE_INT, 2, 0x02), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip wcd939x_regmap_irq_chip = { + .name = "wcd939x", + .irqs = wcd939x_irqs, + .num_irqs = ARRAY_SIZE(wcd939x_irqs), + .num_regs = 3, + .status_base = WCD939X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD939X_DIGITAL_INTR_MASK_0, + .ack_base = WCD939X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .runtime_pm = true, + .irq_drv_data = NULL, +}; + +static int wcd939x_get_clk_rate(int mode) +{ + int rate; + + switch (mode) { + case ADC_MODE_ULP2: + rate = SWR_CLK_RATE_0P6MHZ; + break; + case ADC_MODE_ULP1: + rate = SWR_CLK_RATE_1P2MHZ; + break; + case ADC_MODE_LP: + rate = SWR_CLK_RATE_4P8MHZ; + break; + case ADC_MODE_NORMAL: + case ADC_MODE_LO_HIF: + case ADC_MODE_HIFI: + case ADC_MODE_INVALID: + default: + rate = SWR_CLK_RATE_9P6MHZ; + break; + } + + return rate; +} + +static int wcd939x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank) +{ + u8 mask = (bank ? 0xF0 : 0x0F); + u8 val = 0; + + switch (rate) { + case SWR_CLK_RATE_0P6MHZ: + val = 6; + break; + case SWR_CLK_RATE_1P2MHZ: + val = 5; + break; + case SWR_CLK_RATE_2P4MHZ: + val = 3; + break; + case SWR_CLK_RATE_4P8MHZ: + val = 1; + break; + case SWR_CLK_RATE_9P6MHZ: + default: + val = 0; + break; + } + + snd_soc_component_write_field(component, WCD939X_DIGITAL_SWR_TX_CLK_RATE, mask, val); + + return 0; +} + +static int wcd939x_io_init(struct snd_soc_component *component) +{ + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_ANALOG_BIAS_EN, true); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, true); + + /* 10 msec delay as per HW requirement */ + usleep_range(10000, 10010); + snd_soc_component_write_field(component, WCD939X_ANA_BIAS, + WCD939X_BIAS_PRECHRG_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x15); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x15); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_ICTRL_SCBIAS_ULP0P6M, 1); + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2CASC_ULP, + WCD939X_FE_ICTRL_STG2CASC_ULP_VALUE, 4); + + snd_soc_component_write_field(component, WCD939X_TX_COM_NEW_INT_FE_ICTRL_STG2MAIN_ULP, + WCD939X_FE_ICTRL_STG2MAIN_ULP_VALUE, 8); + + snd_soc_component_write_field(component, WCD939X_MICB1_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB2_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB3_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_MICB4_TEST_CTL_1, + WCD939X_TEST_CTL_1_NOISE_FILT_RES_VAL, 7); + snd_soc_component_write_field(component, WCD939X_TX_3_4_TEST_BLK_EN2, + WCD939X_TEST_BLK_EN2_TXFE2_MBHC_CLKRST_EN, false); + + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_OCP_FSM_EN, true); + snd_soc_component_write_field(component, WCD939X_HPH_OCP_CTL, + WCD939X_OCP_CTL_SCD_OP_EN, true); + + snd_soc_component_write(component, WCD939X_E_CFG0, + WCD939X_CFG0_IDLE_STEREO | + WCD939X_CFG0_AUTO_DISABLE_ANC); + + return 0; +} + +static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, + struct sdw_port_config *port_config, + u8 enable) +{ + u8 ch_mask, port_num; + + port_num = ch_info->port_num; + ch_mask = ch_info->ch_mask; + + port_config->num = port_num; + + if (enable) + port_config->ch_mask |= ch_mask; + else + port_config->ch_mask &= ~ch_mask; + + return 0; +} + +static int wcd939x_connect_port(struct wcd939x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable) +{ + return wcd939x_sdw_connect_port(&wcd->ch_info[ch_id], + &wcd->port_config[port_num - 1], + enable); +} + +static int wcd939x_codec_enable_rxclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, true); + + /* Analog path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + true); + + /* Digital path clock controls */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VNEG_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_VPOS_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD2_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD1_CLK_EN, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_RXD0_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV4_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_RX_CLK_EN, false); + + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_RX_BIAS_ENABLE, false); + + break; + } + + return 0; +} + +static int wcd939x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 0x1d); + if (wcd939x->comp1_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp2_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN)) + usleep_range(5000, 5010); + + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_L_EN, + WCD939X_L_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD939X_RDAC_HD2_CTL_L_HD2_RES_DIV_CTL_L, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHL_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_RDAC_CLK_CTL1, + WCD939X_RDAC_CLK_CTL1_OPAMP_CHOP_CLK_EN, + false); + + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, true); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 0x1d); + if (wcd939x->comp2_enable) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + true); + /* 5msec compander delay as per HW requirement */ + if (!wcd939x->comp1_enable || + snd_soc_component_read_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHL_COMP_EN)) + usleep_range(5000, 5010); + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, + false); + } else { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_COMP_CTL_0, + WCD939X_CDC_COMP_CTL_0_HPHR_COMP_EN, + false); + snd_soc_component_write_field(component, WCD939X_HPH_R_EN, + WCD939X_R_EN_GAIN_SOURCE_SEL, true); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD939X_RDAC_HD2_CTL_R_HD2_RES_DIV_CTL_R, 1); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD939X_CDC_HPH_GAIN_CTL_HPHR_RX_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_EAR_GAIN_CTL, + WCD939X_CDC_EAR_GAIN_CTL_EAR_EN, true); + + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, false); + + /* 5 msec delay as per HW requirement */ + usleep_range(5000, 5010); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_EAR_DAC_CON, + WCD939X_DAC_CON_DAC_SAMPLE_EDGE_SEL, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + + enable_irq(wcd939x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_HPHR_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHR_PA_OFF); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL1, + WCD939X_PDM_WD_CTL1_PDM_WD_EN, 0); + + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd939x->hph_mode; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, true); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + wcd_clsh_set_hph_mode(wcd939x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + true); + if (hph_mode == CLS_H_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_PWR_LEVEL, 0); + + snd_soc_component_write_field(component, WCD939X_FLYBACK_VNEG_CTRL_4, + WCD939X_VNEG_CTRL_4_ILIM_SEL, 0xd); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, true); + + if (snd_soc_component_read_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHR_REF_ENABLE)) + usleep_range(2500, 2600); /* 2.5msec delay as per HW requirement */ + + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD939X_HPH_REFBUFF_LP_CTL, + WCD939X_REFBUFF_LP_CTL_PREREF_FILT_BYPASS, + false); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + snd_soc_component_write_field(component, WCD939X_HPH_NEW_INT_TIMER1, + WCD939X_TIMER1_AUTOCHOP_TIMER_CTL_EN, true); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, + true); + enable_irq(wcd939x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd939x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_ENABLE, false); + + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd939x->status_mask)) { + if (!wcd939x->comp1_enable) + usleep_range(21000, 21100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd939x->status_mask); + } + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_write_field(component, WCD939X_ANA_HPH, + WCD939X_HPH_HPHL_REF_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (wcd939x->ldoh) + snd_soc_component_write_field(component, WCD939X_LDOH_MODE, + WCD939X_MODE_LDOH_EN, false); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable watchdog interrupt for HPHL */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 3); + /* For EAR, use CLASS_AB regulator mode */ + snd_soc_component_write_field(component, WCD939X_ANA_RX_SUPPLIES, + WCD939X_RX_SUPPLIES_REGULATOR_MODE, true); + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 6 msec delay as per HW requirement */ + usleep_range(6000, 6010); + enable_irq(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_ANA_EAR_COMPANDER_CTL, + WCD939X_EAR_COMPANDER_CTL_GAIN_OVRD_REG, + false); + /* 7 msec delay as per HW requirement */ + usleep_range(7000, 7010); + snd_soc_component_write_field(component, WCD939X_DIGITAL_PDM_WD_CTL0, + WCD939X_PDM_WD_CTL0_PDM_WD_EN, 0); + wcd_clsh_ctrl_set_state(wcd939x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_AB_HIFI); + break; + } + + return 0; +} + +/* TX Controls */ + +static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 dmic_clk_reg, dmic_clk_en_reg; + u8 dmic_clk_en_mask; + u8 dmic_ctl_mask; + u8 dmic_clk_mask; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC1_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC1_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC1_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC1_IN_SEL; + break; + case 2: + case 3: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC2_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC2_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_1_2_DMIC2_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC3_IN_SEL; + break; + case 4: + case 5: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC3_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC3_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC3_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC4_IN_SEL; + break; + case 6: + case 7: + dmic_clk_reg = WCD939X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD939X_DIGITAL_CDC_DMIC4_CTL; + dmic_clk_en_mask = WCD939X_CDC_DMIC4_CTL_DMIC_CLK_EN; + dmic_clk_mask = WCD939X_CDC_DMIC_RATE_3_4_DMIC4_RATE; + dmic_ctl_mask = WCD939X_CDC_AMIC_CTL_AMIC5_IN_SEL; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, false); + /* 250us sleep as per HW requirement */ + usleep_range(250, 260); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + true); + /* Setting DMIC clock rate to 2.4MHz */ + snd_soc_component_write_field(component, dmic_clk_reg, + dmic_clk_mask, 3); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, true); + /* enable clock scaling */ + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_CLK_SCALE_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_DMIC_CTL, + WCD939X_CDC_DMIC_CTL_DMIC_DIV_BAK_EN, true); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_AMIC_CTL, + dmic_ctl_mask, 1); + if (w->shift == 2) + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DMIC2_CTL, + WCD939X_CDC_DMIC2_CTL_DMIC_LEFT_EN, + false); + snd_soc_component_write_field(component, dmic_clk_en_reg, + dmic_clk_en_mask, 0); + break; + } + return 0; +} + +static int wcd939x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int bank; + int rate; + + bank = wcd939x_swr_get_current_bank(wcd939x->sdw_priv[AIF1_CAP]->sdev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + int mode = 0; + + if (test_bit(WCD_ADC1, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC1]]; + if (test_bit(WCD_ADC2, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC2]]; + if (test_bit(WCD_ADC3, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC3]]; + if (test_bit(WCD_ADC4, &wcd939x->status_mask)) + mode |= tx_mode_bit[wcd939x->tx_mode[WCD_ADC4]]; + + if (mode) + rate = wcd939x_get_clk_rate(ffs(mode) - 1); + else + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, bank); + wcd939x_set_swr_clk_rate(component, rate, !bank); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + rate = wcd939x_get_clk_rate(ADC_MODE_INVALID); + wcd939x_set_swr_clk_rate(component, rate, !bank); + wcd939x_set_swr_clk_rate(component, rate, bank); + } + break; + } + + return 0; +} + +static int wcd939x_get_adc_mode(int val) +{ + int ret = 0; + + switch (val) { + case ADC_MODE_INVALID: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_HIFI: + ret = ADC_MODE_VAL_HIFI; + break; + case ADC_MODE_LO_HIF: + ret = ADC_MODE_VAL_LO_HIF; + break; + case ADC_MODE_NORMAL: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_LP: + ret = ADC_MODE_VAL_LP; + break; + case ADC_MODE_ULP1: + ret = ADC_MODE_VAL_ULP1; + break; + case ADC_MODE_ULP2: + ret = ADC_MODE_VAL_ULP2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd939x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + set_bit(w->shift, &wcd939x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + false); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_CLK_EN, + false); + clear_bit(w->shift, &wcd939x->status_mask); + break; + } + + return 0; +} + +static void wcd939x_tx_channel_config(struct snd_soc_component *component, + int channel, bool init) +{ + int reg, mask; + + switch (channel) { + case 0: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF1_INIT; + break; + case 1: + reg = WCD939X_ANA_TX_CH2; + mask = WCD939X_TX_CH2_HPF2_INIT; + break; + case 2: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF3_INIT; + break; + case 3: + reg = WCD939X_ANA_TX_CH4; + mask = WCD939X_TX_CH4_HPF4_INIT; + break; + default: + return; + } + + snd_soc_component_write_field(component, reg, mask, init); +} + +static int wcd939x_adc_enable_req(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_FS_RATE_4P8, true); + snd_soc_component_write_field(component, WCD939X_DIGITAL_CDC_REQ_CTL, + WCD939X_CDC_REQ_CTL_NO_NOTCH, false); + + wcd939x_tx_channel_config(component, w->shift, true); + mode = wcd939x_get_adc_mode(wcd939x->tx_mode[w->shift]); + if (mode < 0) { + dev_info(component->dev, "Invalid ADC mode\n"); + return -EINVAL; + } + + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + true); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + true); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + true); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + mode); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + true); + break; + default: + break; + } + + wcd939x_tx_channel_config(component, w->shift, false); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD0_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, + false); + break; + case 1: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD939X_CDC_TX_ANA_MODE_0_1_TXD1_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, + false); + break; + case 2: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD2_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, + false); + break; + case 3: + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD939X_CDC_TX_ANA_MODE_2_3_TXD3_MODE, + false); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, + false); + break; + default: + break; + } + break; + } + + return 0; +} + +static int wcd939x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_field; + u16 micb_reg; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_field = WCD939X_MICB1_ENABLE; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_field = WCD939X_MICB2_ENABLE; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_field = WCD939X_MICB3_ENABLE; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_field = WCD939X_MICB4_ENABLE; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + }; + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd939x->pullup_ref[micb_index]++; + if (wcd939x->pullup_ref[micb_index] == 1 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + break; + case MICB_PULLUP_DISABLE: + if (wcd939x->pullup_ref[micb_index] > 0) + wcd939x->pullup_ref[micb_index]--; + if (wcd939x->pullup_ref[micb_index] == 0 && + wcd939x->micb_ref[micb_index] == 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + break; + case MICB_ENABLE: + wcd939x->micb_ref[micb_index]++; + if (wcd939x->micb_ref[micb_index] == 1) { + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD3_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD2_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD1_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_DIG_CLK_CTL, + WCD939X_CDC_DIG_CLK_CTL_TXD0_CLK_EN, true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_CLK_CTL, + WCD939X_CDC_ANA_CLK_CTL_ANA_TX_DIV2_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_DIGITAL_CDC_ANA_TX_CLK_CTL, + WCD939X_CDC_ANA_TX_CLK_CTL_ANA_TXSCBIAS_CLK_EN, + true); + snd_soc_component_write_field(component, + WCD939X_MICB1_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB2_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB3_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, + WCD939X_MICB4_TEST_CTL_2, + WCD939X_TEST_CTL_2_IBIAS_LDO_DRIVER, true); + snd_soc_component_write_field(component, micb_reg, micb_field, + MICB_BIAS_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + } + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + break; + case MICB_DISABLE: + if (wcd939x->micb_ref[micb_index] > 0) + wcd939x->micb_ref[micb_index]--; + + if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] > 0) + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_PULL_UP); + else if (wcd939x->micb_ref[micb_index] == 0 && + wcd939x->pullup_ref[micb_index] == 0) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_write_field(component, micb_reg, + micb_field, MICB_BIAS_DISABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd939x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd939x_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd939x_tx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.enumerated.item[0] = wcd939x->tx_mode[path]; + + return 0; +} + +static int wcd939x_tx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + if (wcd939x->tx_mode[path] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd939x->tx_mode[path] = ucontrol->value.enumerated.item[0]; + + return 1; +} + +/* RX Controls */ + +static int wcd939x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->hph_mode; + + return 0; +} + +static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == wcd939x->hph_mode) + return 0; + + if (wcd939x->variant == WCD9390) { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } else { + switch (mode_val) { + case CLS_H_NORMAL: + case CLS_H_HIFI: + case CLS_H_LP: + case CLS_AB: + case CLS_H_LOHIFI: + case CLS_H_ULP: + case CLS_AB_HIFI: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + wcd939x->hph_mode = mode_val; + return 1; + } + } + + dev_dbg(component->dev, "%s: Invalid HPH Mode\n", __func__); + return -EINVAL; +} + +static int wcd939x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (mc->shift) + ucontrol->value.integer.value[0] = wcd939x->comp2_enable ? 1 : 0; + else + ucontrol->value.integer.value[0] = wcd939x->comp1_enable ? 1 : 0; + + return 0; +} + +static int wcd939x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[AIF1_PB]; + bool value = !!ucontrol->value.integer.value[0]; + int portidx = wcd->ch_info[mc->reg].port_num; + + if (mc->shift) + wcd939x->comp2_enable = value; + else + wcd939x->comp1_enable = value; + + if (value) + wcd939x_connect_port(wcd, portidx, mc->reg, true); + else + wcd939x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd939x_ldoh_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd939x->ldoh ? 1 : 0; + + return 0; +} + +static int wcd939x_ldoh_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->ldoh == !!ucontrol->value.integer.value[0]) + return 0; + + wcd939x->ldoh = !!ucontrol->value.integer.value[0]; + + return 1; +} + +static const char * const tx_mode_mux_text_wcd9390[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", +}; + +static const struct soc_enum tx0_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx1_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx2_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const struct soc_enum tx3_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9390), + tx_mode_mux_text_wcd9390); + +static const char * const tx_mode_mux_text[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", + "ADC_ULP1", "ADC_ULP2", +}; + +static const struct soc_enum tx0_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx1_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx2_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx3_mode_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const char * const rx_hph_mode_mux_text_wcd9390[] = { + "CLS_H_NORMAL", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP", + "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum_wcd9390 = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9390), + rx_hph_mode_mux_text_wcd9390); + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_NORMAL", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct snd_kcontrol_new wcd9390_snd_controls[] = { + SOC_SINGLE_TLV("EAR_PA Volume", WCD939X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9390, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum_wcd9390, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new wcd9395_snd_controls[] = { + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd939x_rx_hph_mode_get, wcd939x_rx_hph_mode_put), + + SOC_ENUM_EXT("TX0 MODE", tx0_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_mux_enum, + wcd939x_tx_mode_get, wcd939x_tx_mode_put), +}; + +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic7_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic8_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const char * const adc1_mux_text[] = { + "CH1_AMIC_DISABLE", "CH1_AMIC1", "CH1_AMIC2", "CH1_AMIC3", "CH1_AMIC4", "CH1_AMIC5" +}; + +static const struct soc_enum adc1_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 0, + ARRAY_SIZE(adc1_mux_text), adc1_mux_text); + +static const struct snd_kcontrol_new tx_adc1_mux = + SOC_DAPM_ENUM("ADC1 MUX Mux", adc1_enum); + +static const char * const adc2_mux_text[] = { + "CH2_AMIC_DISABLE", "CH2_AMIC1", "CH2_AMIC2", "CH2_AMIC3", "CH2_AMIC4", "CH2_AMIC5" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH12_MUX, 3, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const char * const adc3_mux_text[] = { + "CH3_AMIC_DISABLE", "CH3_AMIC1", "CH3_AMIC3", "CH3_AMIC4", "CH3_AMIC5" +}; + +static const struct soc_enum adc3_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 0, + ARRAY_SIZE(adc3_mux_text), adc3_mux_text); + +static const struct snd_kcontrol_new tx_adc3_mux = + SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum); + +static const char * const adc4_mux_text[] = { + "CH4_AMIC_DISABLE", "CH4_AMIC1", "CH4_AMIC3", "CH4_AMIC4", "CH4_AMIC5" +}; + +static const struct soc_enum adc4_enum = + SOC_ENUM_SINGLE(WCD939X_TX_NEW_CH34_MUX, 3, + ARRAY_SIZE(adc4_mux_text), adc4_mux_text); + +static const struct snd_kcontrol_new tx_adc4_mux = + SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum); + +static const char * const rdac3_mux_text[] = { + "RX3", "RX1" +}; + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD939X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new rx_rdac3_mux = + SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static int wcd939x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx] ? 1 : 0; + + return 0; +} + +static const char *version_to_str(u32 version) +{ + switch (version) { + case WCD939X_VERSION_1_0: + return __stringify(WCD939X_1_0); + case WCD939X_VERSION_1_1: + return __stringify(WCD939X_1_1); + case WCD939X_VERSION_2_0: + return __stringify(WCD939X_2_0); + } + return NULL; +} + +static int wcd939x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(comp); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[mixer->shift]; + unsigned int portidx = wcd->ch_info[mixer->reg].port_num; + + wcd->port_enable[portidx] = !!ucontrol->value.integer.value[0]; + + wcd939x_connect_port(wcd, portidx, mixer->reg, wcd->port_enable[portidx]); + + return 1; +} + +/* MBHC Related */ + +static void wcd939x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_1, + WCD939X_CTL_1_RCO_EN, enable); +} + +static void wcd939x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_BIAS_EN, enable); +} + +static void wcd939x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = (btn_high[i] * 2) / 25; + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_BTN0 + i, + WCD939X_MBHC_BTN0_VTH, vth); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd939x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + + if (micb_num == MIC_BIAS_2) { + u8 val; + + val = FIELD_GET(WCD939X_MICB2_ENABLE, + snd_soc_component_read(component, WCD939X_ANA_MICB2)); + if (val == MICB_BIAS_ENABLE) + return true; + } + + return false; +} + +static void wcd939x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || + pull_up_cur < HS_PULLUP_I_3P0_UA || + pull_up_cur == HS_PULLUP_I_DEFAULT) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + dev_dbg(component->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD939X_MECH_DET_CURRENT_HSDET_PULLUP_CTL, pull_up_cur); +} + +static int wcd939x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd939x_micbias_control(component, micb_num, req, false); +} + +static void wcd939x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 3); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_RAMP_ENABLE, false); + snd_soc_component_write_field(component, WCD939X_ANA_MICB2_RAMP, + WCD939X_MICB2_RAMP_SHIFT_CTL, 0); + } +} + +static int wcd939x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static int wcd939x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + unsigned int micb_en_field, micb_vout_ctl_field; + unsigned int micb_reg, cur_vout_ctl, micb_en; + int req_vout_ctl; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD939X_ANA_MICB1; + micb_en_field = WCD939X_MICB1_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_2: + micb_reg = WCD939X_ANA_MICB2; + micb_en_field = WCD939X_MICB2_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + case MIC_BIAS_3: + micb_reg = WCD939X_ANA_MICB3; + micb_en_field = WCD939X_MICB3_ENABLE; + micb_vout_ctl_field = WCD939X_MICB1_VOUT_CTL; + break; + case MIC_BIAS_4: + micb_reg = WCD939X_ANA_MICB4; + micb_en_field = WCD939X_MICB4_ENABLE; + micb_vout_ctl_field = WCD939X_MICB2_VOUT_CTL; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd939x->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + micb_en_field); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + micb_vout_ctl_field); + + req_vout_ctl = wcd939x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = req_vout_ctl; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == MICB_BIAS_ENABLE) + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_PULL_DOWN); + + snd_soc_component_write_field(component, micb_reg, + micb_vout_ctl_field, req_vout_ctl); + + if (micb_en == MICB_BIAS_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + micb_en_field, MICB_BIAS_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + +exit: + mutex_unlock(&wcd939x->micb_lock); + return ret; +} + +static int wcd939x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + int micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd939x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd939x->micb2_mv; + + return wcd939x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +/* Selected by WCD939X_MBHC_GET_C1() */ +static const s16 wcd939x_wcd_mbhc_d1_a[4] = { + 0, 30, 30, 6 +}; + +/* Selected by zdet_param.noff */ +static const int wcd939x_mbhc_mincode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 +}; + +static const struct zdet_param wcd939x_mbhc_zdet_param = { + .ldo_ctl = 4, + .noff = 0, + .nshift = 6, + .btn5 = 0x18, + .btn6 = 0x60, + .btn7 = 0x78, +}; + +static void wcd939x_mbhc_get_result_params(struct snd_soc_component *component, + int32_t *zdet) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 x1, d1, denom; + int val; + s16 c1; + int i; + + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, true); + for (i = 0; i < WCD939X_ZDET_NUM_MEASUREMENTS; i++) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + if (val & BIT(7)) + break; + } + val = val << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_CHG_EN, false); + x1 = WCD939X_MBHC_GET_X1(val); + c1 = WCD939X_MBHC_GET_C1(val); + + /* If ramp is not complete, give additional 5ms */ + if (c1 < 2 && x1) + mdelay(5); + + if (!c1 || !x1) { + dev_dbg(component->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + + d1 = wcd939x_wcd_mbhc_d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - zdet_param->noff)); + if (denom > 0) + *zdet = (WCD939X_ANA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < wcd939x_mbhc_mincode_param[zdet_param->noff]) + *zdet = WCD939X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + val = snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_1, + WCD939X_MBHC_RESULT_1_Z_RESULT_LSB) << 8; + val |= snd_soc_component_read_field(component, WCD939X_ANA_MBHC_RESULT_2, + WCD939X_MBHC_RESULT_2_Z_RESULT_MSB); + x1 = WCD939X_MBHC_GET_X1(val); + i++; + if (i == WCD939X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd939x_mbhc_zdet_ramp(struct snd_soc_component *component, + s32 *zl, int32_t *zr) +{ + const struct zdet_param *zdet_param = &wcd939x_mbhc_zdet_param; + s32 zdet = 0; + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_MAXV_CTL, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN5, WCD939X_MBHC_BTN5_VTH, + zdet_param->btn5); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN6, WCD939X_MBHC_BTN6_VTH, + zdet_param->btn6); + snd_soc_component_update_bits(component, WCD939X_ANA_MBHC_BTN7, WCD939X_MBHC_BTN7_VTH, + zdet_param->btn7); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, + WCD939X_ZDET_ANA_CTL_RANGE_CTL, zdet_param->noff); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_TIME_CTL, zdet_param->nshift); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_ZDET_RAMP_CTL, + WCD939X_ZDET_RAMP_CTL_ACC1_MIN_CTL, 6); /*acc1_min_63 */ + + if (!zl) + goto z_right; + + /* Start impedance measurement for HPH_L */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_L_MEAS_EN, false); + + *zl = zdet; + +z_right: + if (!zr) + return; + + /* Start impedance measurement for HPH_R */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, true); + dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd939x_mbhc_get_result_params(component, &zdet); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ZDET, + WCD939X_MBHC_ZDET_ZDET_R_MEAS_EN, false); + + *zr = zdet; +} + +static void wcd939x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + s32 *z_val, int flag_l_r) +{ + int q1_cal; + s16 q1; + + q1 = snd_soc_component_read(component, WCD939X_DIGITAL_EFUSE_REG_21 + flag_l_r); + if (q1 & BIT(7)) + q1_cal = (10000 - ((q1 & GENMASK(6, 0)) * 10)); + else + q1_cal = (10000 + (q1 * 10)); + + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd939x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + u32 *zl, uint32_t *zr) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(component->dev); + unsigned int reg0, reg1, reg2, reg3, reg4; + int z_mono, z_diff1, z_diff2; + bool is_fsm_disable = false; + s32 z1l, z1r, z1ls; + + reg0 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD939X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD939X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN)) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, false); + is_fsm_disable = true; + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, false); + + /* Turn off 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, + false); + + /* + * Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, false); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, false); + + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + wcd939x_mbhc_zdet_ramp(component, &z1l, NULL); + if (z1l == WCD939X_ZDET_FLOATING_IMPEDANCE || z1l > WCD939X_ZDET_VAL_100K) { + *zl = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zl = z1l / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd939x_mbhc_zdet_ramp(component, NULL, &z1r); + if (z1r == WCD939X_ZDET_FLOATING_IMPEDANCE || z1r > WCD939X_ZDET_VAL_100K) { + *zr = WCD939X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1r / 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE && + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + + if (*zl == WCD939X_ZDET_FLOATING_IMPEDANCE || + *zr == WCD939X_ZDET_FLOATING_IMPEDANCE || + (*zl < WCD_MONO_HS_MIN_THR && *zr > WCD_MONO_HS_MIN_THR) || + (*zl > WCD_MONO_HS_MIN_THR && *zr < WCD_MONO_HS_MIN_THR)) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, true); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, true); + wcd939x_mbhc_zdet_ramp(component, &z1ls, NULL); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, false); + snd_soc_component_write_field(component, WCD939X_HPH_R_ATEST, + WCD939X_R_ATEST_HPH_GND_OVR, false); + + z1ls /= 1000; + wcd939x_wcd_mbhc_qfuse_cal(component, &z1ls, 0); + + /* Parallel of left Z and 9 ohm pull down resistor */ + z_mono = (*zl * 9) / (*zl + 9); + z_diff1 = z1ls > z_mono ? z1ls - z_mono : z_mono - z1ls; + z_diff2 = *zl > z1ls ? *zl - z1ls : z1ls - *zl; + if ((z_diff1 * (*zl + z1ls)) > (z_diff2 * (z1ls + z_mono))) { + dev_dbg(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd939x->wcd_mbhc, WCD_MBHC_HPH_MONO); + } + + /* Enable surge protection again after impedance detection */ + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHR, true); + snd_soc_component_write_field(component, WCD939X_HPH_SURGE_EN, + WCD939X_EN_EN_SURGE_PROTECTION_HPHL, true); + +zdet_complete: + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD939X_ANA_MBHC_BTN7, reg2); + + /* Turn on 100k pull down on HPHL */ + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_SW_HPH_L_P_100K_TO_GND, true); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd939x->mbhc_cfg.hphl_swh) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_L_DET_EN, true); + + snd_soc_component_write(component, WCD939X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD939X_MBHC_CTL_CLK, reg3); + + if (is_fsm_disable) + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_ELECT, + WCD939X_MBHC_ELECT_FSM_EN, true); +} + +static void wcd939x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + true); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, true); + } else { + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_GND_DET_EN, false); + snd_soc_component_write_field(component, WCD939X_ANA_MBHC_MECH, + WCD939X_MBHC_MECH_MECH_HS_G_PULLUP_COMP_EN, + false); + } +} + +static void wcd939x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_R, enable); + snd_soc_component_write_field(component, WCD939X_HPH_PA_CTL2, + WCD939X_PA_CTL2_HPHPA_GND_L, enable); +} + +static void wcd939x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, wcd939x->mbhc_cfg.moist_rref); +} + +static void wcd939x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, + wcd939x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); +} + +static bool wcd939x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd939x->mbhc_cfg.moist_rref == R_OFF || wcd939x->typec_analog_mux) { + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd939x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD939X_MBHC_NEW_CTL_2, + WCD939X_CTL_2_M_RTH_CTL)) + goto done; + + wcd939x_mbhc_moisture_detect_en(component, true); + + /* Read moisture comparator status, invert of status bit */ + ret = !snd_soc_component_read_field(component, WCD939X_MBHC_NEW_FSM_STATUS, + WCD939X_FSM_STATUS_HS_M_COMP_STATUS); +done: + return ret; +} + +static void wcd939x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD939X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD939X_MOISTURE_DET_POLLING_CTRL_MOIST_EN_POLLING, + enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd939x_mbhc_clk_setup, + .mbhc_bias = wcd939x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd939x_mbhc_program_btn_thr, + .micbias_enable_status = wcd939x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd939x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd939x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd939x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd939x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd939x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd939x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd939x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd939x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd939x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd939x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd939x_mbhc_moisture_detect_en, +}; + +static int wcd939x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd939x->wcd_mbhc); + + return 0; +} + +static int wcd939x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)(kcontrol->private_value); + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + bool hphr = mc->shift; + u32 zl, zr; + + wcd_mbhc_get_impedance(wcd939x->wcd_mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + wcd939x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + wcd939x_hph_impedance_get, NULL), +}; + +static int wcd939x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd939x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_OCP_INT); + + wcd939x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd939x->wcd_mbhc)) + return PTR_ERR(wcd939x->wcd_mbhc); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd939x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd939x->wcd_mbhc); +} + +/* END MBHC */ + +static const struct snd_kcontrol_new wcd939x_snd_controls[] = { + /* RX Path */ + SOC_SINGLE_EXT("HPHL_COMP Switch", WCD939X_COMP_L, 0, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", WCD939X_COMP_R, 1, 1, 0, + wcd939x_get_compander, wcd939x_set_compander), + SOC_SINGLE_EXT("HPHL Switch", WCD939X_HPH_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD939X_HPH_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("CLSH Switch", WCD939X_CLSH, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("LO Switch", WCD939X_LO, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_L Switch", WCD939X_DSD_L, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DSD_R Switch", WCD939X_DSD_R, 0, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("HPHL Volume", WCD939X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD939X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, + wcd939x_ldoh_get, wcd939x_ldoh_put), + + /* TX Path */ + SOC_SINGLE_EXT("ADC1 Switch", WCD939X_ADC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD939X_ADC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD939X_ADC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("ADC4 Switch", WCD939X_ADC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD939X_DMIC0, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD939X_DMIC1, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD939X_MBHC, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD939X_DMIC2, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD939X_DMIC3, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD939X_DMIC4, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD939X_DMIC5, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC6 Switch", WCD939X_DMIC6, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_EXT("DMIC7 Switch", WCD939X_DMIC7, 1, 1, 0, + wcd939x_get_swr_port, wcd939x_set_swr_port), + SOC_SINGLE_TLV("ADC1 Volume", WCD939X_ANA_TX_CH1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD939X_ANA_TX_CH2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD939X_ANA_TX_CH3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD939X_ANA_TX_CH4, 0, 20, 0, + analog_gain), +}; + +static const struct snd_soc_dapm_widget wcd939x_dapm_widgets[] = { + /*input widgets*/ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_MIC("Analog Mic1", NULL), + SND_SOC_DAPM_MIC("Analog Mic2", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + /* TX widgets */ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0, + wcd939x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0, + wcd939x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC1 MUX", SND_SOC_NOPM, 0, 0, &tx_adc1_mux), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux), + SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux), + + /* tx mixers */ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, + adc1_switch, ARRAY_SIZE(adc1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, + adc2_switch, ARRAY_SIZE(adc2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, + adc3_switch, ARRAY_SIZE(adc3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0, + adc4_switch, ARRAY_SIZE(adc4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, + dmic1_switch, ARRAY_SIZE(dmic1_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0, + dmic2_switch, ARRAY_SIZE(dmic2_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0, + dmic3_switch, ARRAY_SIZE(dmic3_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0, + dmic4_switch, ARRAY_SIZE(dmic4_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0, + dmic5_switch, ARRAY_SIZE(dmic5_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0, + dmic6_switch, ARRAY_SIZE(dmic6_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0, + dmic7_switch, ARRAY_SIZE(dmic7_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0, + dmic8_switch, ARRAY_SIZE(dmic8_switch), wcd939x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* micbias widgets */ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* micbias pull up widgets */ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd939x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* output widgets tx */ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"), + + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_EAR"), + + /* rx widgets */ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD939X_ANA_EAR, 7, 0, NULL, 0, + wcd939x_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PGA", WCD939X_ANA_HPH, 7, 0, NULL, 0, + wcd939x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD939X_ANA_HPH, 6, 0, NULL, 0, + wcd939x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd939x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, + wcd939x_codec_enable_rxclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + + /* rx mixer widgets */ + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /* output widgets rx */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), +}; + +static const struct snd_soc_dapm_route wcd939x_audio_map[] = { + /* TX Path */ + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1 REQ"}, + {"ADC1 REQ", NULL, "ADC1"}, + {"ADC1", NULL, "ADC1 MUX"}, + {"ADC1 MUX", "CH1_AMIC1", "AMIC1"}, + {"ADC1 MUX", "CH1_AMIC2", "AMIC2"}, + {"ADC1 MUX", "CH1_AMIC3", "AMIC3"}, + {"ADC1 MUX", "CH1_AMIC4", "AMIC4"}, + {"ADC1 MUX", "CH1_AMIC5", "AMIC5"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2 REQ"}, + {"ADC2 REQ", NULL, "ADC2"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "CH2_AMIC1", "AMIC1"}, + {"ADC2 MUX", "CH2_AMIC2", "AMIC2"}, + {"ADC2 MUX", "CH2_AMIC3", "AMIC3"}, + {"ADC2 MUX", "CH2_AMIC4", "AMIC4"}, + {"ADC2 MUX", "CH2_AMIC5", "AMIC5"}, + + {"ADC3_OUTPUT", NULL, "ADC3_MIXER"}, + {"ADC3_MIXER", "Switch", "ADC3 REQ"}, + {"ADC3 REQ", NULL, "ADC3"}, + {"ADC3", NULL, "ADC3 MUX"}, + {"ADC3 MUX", "CH3_AMIC1", "AMIC1"}, + {"ADC3 MUX", "CH3_AMIC3", "AMIC3"}, + {"ADC3 MUX", "CH3_AMIC4", "AMIC4"}, + {"ADC3 MUX", "CH3_AMIC5", "AMIC5"}, + + {"ADC4_OUTPUT", NULL, "ADC4_MIXER"}, + {"ADC4_MIXER", "Switch", "ADC4 REQ"}, + {"ADC4 REQ", NULL, "ADC4"}, + {"ADC4", NULL, "ADC4 MUX"}, + {"ADC4 MUX", "CH4_AMIC1", "AMIC1"}, + {"ADC4 MUX", "CH4_AMIC3", "AMIC3"}, + {"ADC4 MUX", "CH4_AMIC4", "AMIC4"}, + {"ADC4 MUX", "CH4_AMIC5", "AMIC5"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, + + {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, + {"DMIC3_MIXER", "Switch", "DMIC3"}, + + {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, + {"DMIC4_MIXER", "Switch", "DMIC4"}, + + {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, + {"DMIC5_MIXER", "Switch", "DMIC5"}, + + {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, + {"DMIC6_MIXER", "Switch", "DMIC6"}, + + {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"}, + {"DMIC7_MIXER", "Switch", "DMIC7"}, + + {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"}, + {"DMIC8_MIXER", "Switch", "DMIC8"}, + + /* RX Path */ + {"IN1_HPHL", NULL, "VDD_BUCK"}, + {"IN1_HPHL", NULL, "CLS_H_PORT"}, + + {"RX1", NULL, "IN1_HPHL"}, + {"RX1", NULL, "RXCLK"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "VDD_BUCK"}, + {"IN2_HPHR", NULL, "CLS_H_PORT"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"RX2", NULL, "RXCLK"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"IN3_EAR", NULL, "VDD_BUCK"}, + {"RX3", NULL, "IN3_EAR"}, + {"RX3", NULL, "RXCLK"}, + + {"RDAC3_MUX", "RX3", "RX3"}, + {"RDAC3_MUX", "RX1", "RX1"}, + {"RDAC3", NULL, "RDAC3_MUX"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, +}; + +static int wcd939x_set_micbias_data(struct wcd939x_priv *wcd939x) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + + /* set micbias voltage */ + vout_ctl_1 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb1_mv); + vout_ctl_2 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb2_mv); + vout_ctl_3 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb3_mv); + vout_ctl_4 = wcd939x_get_micb_vout_ctl_val(wcd939x->micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0) + return -EINVAL; + + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB1, + WCD939X_MICB1_VOUT_CTL, vout_ctl_1); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB2, + WCD939X_MICB2_VOUT_CTL, vout_ctl_2); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB3, + WCD939X_MICB3_VOUT_CTL, vout_ctl_3); + regmap_update_bits(wcd939x->regmap, WCD939X_ANA_MICB4, + WCD939X_MICB4_VOUT_CTL, vout_ctl_4); + + return 0; +} + +static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) +{ + /* + * HPHR/HPHL/EAR Watchdog interrupt threaded handler + * + * Watchdog interrupts are expected to be enabled when switching + * on the HPHL/R and EAR RX PGA in order to make sure the interrupts + * are acked by the regmap_irq handler to allow PDM sync. + * We could leave those interrupts masked but we would not have + * any valid way to enable/disable them without violating irq layers. + * + * The HPHR/HPHL/EAR Watchdog interrupts are handled + * by regmap_irq, so requesting a threaded handler is the + * safest way to be able to ack those interrupts without + * colliding with the regmap_irq setup. + */ + + return IRQ_HANDLED; +} + +/* + * Setup a virtual interrupt domain to hook regmap_irq + * The root domain will have a single interrupt which mapping + * will trigger the regmap_irq handler. + * + * root: + * wcd_irq_chip + * [0] wcd939x_regmap_irq_chip + * [0] MBHC_BUTTON_PRESS_DET + * [1] MBHC_BUTTON_RELEASE_DET + * ... + * [16] HPHR_SURGE_DET_INT + * + * Interrupt trigger: + * soundwire_interrupt_callback() + * \-handle_nested_irq(0) + * \- regmap_irq_thread() + * \- handle_nested_irq(i) + */ +static struct irq_chip wcd_irq_chip = { + .name = "WCD939x", +}; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev) +{ + wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(wcd->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + return devm_regmap_add_irq_chip(dev, wcd->regmap, + irq_create_mapping(wcd->virq, 0), + IRQF_ONESHOT, 0, &wcd939x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd939x_soc_codec_probe(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int ret, i; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(2000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd939x->regmap); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + wcd939x->variant = snd_soc_component_read_field(component, + WCD939X_DIGITAL_EFUSE_REG_0, + WCD939X_EFUSE_REG_0_WCD939X_ID); + + wcd939x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD939X); + if (IS_ERR(wcd939x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd939x->clsh_info); + } + + wcd939x_io_init(component); + + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd939x_regmap_irq_chip.num_regs; i++) + regmap_write(wcd939x->regmap, + (WCD939X_DIGITAL_INTR_LEVEL_0 + i), 0); + + pm_runtime_put(dev); + + /* Request for watchdog interrupt */ + wcd939x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHR_PDM_WD_INT); + wcd939x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_HPHL_PDM_WD_INT); + wcd939x->ear_pdm_wd_int = regmap_irq_get_virq(wcd939x->irq_chip, + WCD939X_IRQ_EAR_PDM_WD_INT); + + ret = request_threaded_irq(wcd939x->hphr_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret); + goto err_free_clsh_ctrl; + } + + ret = request_threaded_irq(wcd939x->hphl_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret); + goto err_free_hphr_pdm_wd_int; + } + + ret = request_threaded_irq(wcd939x->ear_pdm_wd_int, NULL, wcd939x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WD INT", wcd939x); + if (ret) { + dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret); + goto err_free_hphl_pdm_wd_int; + } + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd939x->hphr_pdm_wd_int); + disable_irq_nosync(wcd939x->hphl_pdm_wd_int); + disable_irq_nosync(wcd939x->ear_pdm_wd_int); + + switch (wcd939x->variant) { + case WCD9390: + ret = snd_soc_add_component_controls(component, wcd9390_snd_controls, + ARRAY_SIZE(wcd9390_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + case WCD9395: + ret = snd_soc_add_component_controls(component, wcd9395_snd_controls, + ARRAY_SIZE(wcd9395_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd939x->variant); + goto err_free_ear_pdm_wd_int; + } + break; + default: + break; + } + + ret = wcd939x_mbhc_init(component); + if (ret) { + dev_err(component->dev, "mbhc initialization failed\n"); + goto err_free_ear_pdm_wd_int; + } + + return 0; + +err_free_ear_pdm_wd_int: + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); +err_free_hphl_pdm_wd_int: + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); +err_free_hphr_pdm_wd_int: + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); +err_free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd939x->clsh_info); + + return ret; +} + +static void wcd939x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + wcd939x_mbhc_deinit(component); + + free_irq(wcd939x->ear_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphl_pdm_wd_int, wcd939x); + free_irq(wcd939x->hphr_pdm_wd_int, wcd939x); + + wcd_clsh_ctrl_free(wcd939x->clsh_info); +} + +static int wcd939x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd939x_priv *wcd = dev_get_drvdata(comp->dev); + + if (jack) + return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + + wcd_mbhc_stop(wcd->wcd_mbhc); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd939x = { + .name = "wcd939x_codec", + .probe = wcd939x_soc_codec_probe, + .remove = wcd939x_soc_codec_remove, + .controls = wcd939x_snd_controls, + .num_controls = ARRAY_SIZE(wcd939x_snd_controls), + .dapm_widgets = wcd939x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd939x_dapm_widgets), + .dapm_routes = wcd939x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd939x_audio_map), + .set_jack = wcd939x_codec_set_jack, + .endianness = 1, +}; + +#if IS_ENABLED(CONFIG_TYPEC) +/* Get USB-C plug orientation to provide swap event for MBHC */ +static int wcd939x_typec_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct wcd939x_priv *wcd939x = typec_switch_get_drvdata(sw); + + wcd939x->typec_orientation = orientation; + + return 0; +} + +static int wcd939x_typec_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct wcd939x_priv *wcd939x = typec_mux_get_drvdata(mux); + unsigned int previous_mode = wcd939x->typec_mode; + + if (!wcd939x->wcd_mbhc) + return -EINVAL; + + if (wcd939x->typec_mode != state->mode) { + wcd939x->typec_mode = state->mode; + + if (wcd939x->typec_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_plug(wcd939x->wcd_mbhc); + else if (previous_mode == TYPEC_MODE_AUDIO) + return wcd_mbhc_typec_report_unplug(wcd939x->wcd_mbhc); + } + + return 0; +} +#endif /* CONFIG_TYPEC */ + +static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int rc = 0; + + rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!rc) + wcd->micb1_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias1 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!rc) + wcd->micb2_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias2 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!rc) + wcd->micb3_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias3 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val); + if (!rc) + wcd->micb4_mv = prop_val / 1000; + else + dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); +} + +#if IS_ENABLED(CONFIG_TYPEC) +static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); + + if (!wcd939x->typec_analog_mux || !wcd939x->typec_switch) + return false; + + /* Report inversion via Type Switch of USBSS */ + typec_switch_set(wcd939x->typec_switch, + wcd939x->typec_orientation == TYPEC_ORIENTATION_REVERSE ? + TYPEC_ORIENTATION_NORMAL : TYPEC_ORIENTATION_REVERSE); + + return true; +} +#endif /* CONFIG_TYPEC */ + +static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device *dev) +{ + struct wcd_mbhc_config *cfg = &wcd939x->mbhc_cfg; +#if IS_ENABLED(CONFIG_TYPEC) + struct device_node *np; +#endif /* CONFIG_TYPEC */ + int ret; + + wcd939x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + if (wcd939x->reset_gpio < 0) + return dev_err_probe(dev, wcd939x->reset_gpio, + "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); + 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"); + } + + wcd939x_dt_parse_micbias_info(dev, wcd939x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD939X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd939x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is node has a port and a valid remote endpoint + * consider HP lines are connected to the USBSS part + */ + np = of_graph_get_remote_node(dev->of_node, 0, 0); + if (np) { + wcd939x->typec_analog_mux = true; + cfg->typec_analog_mux = true; + cfg->swap_gnd_mic = wcd939x_swap_gnd_mic; + } +#endif /* CONFIG_TYPEC */ + + return 0; +} + +static int wcd939x_reset(struct wcd939x_priv *wcd939x) +{ + gpio_direction_output(wcd939x->reset_gpio, 0); + /* 20us sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + gpio_set_value(wcd939x->reset_gpio, 1); + /* 20us sleep required after pulling the reset gpio to HIGH */ + usleep_range(20, 30); + + return 0; +} + +static int wcd939x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd939x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_free(wcd, substream, dai); +} + +static int wcd939x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dai->dev); + struct wcd939x_sdw_priv *wcd = wcd939x->sdw_priv[dai->id]; + + return wcd939x_sdw_set_sdw_stream(wcd, dai, stream, direction); +} + +static const struct snd_soc_dai_ops wcd939x_sdw_dai_ops = { + .hw_params = wcd939x_codec_hw_params, + .hw_free = wcd939x_codec_free, + .set_stream = wcd939x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd939x_dais[] = { + [0] = { + .name = "wcd939x-sdw-rx", + .playback = { + .stream_name = "WCD AIF1 Playback", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd939x_sdw_dai_ops, + }, + [1] = { + .name = "wcd939x-sdw-tx", + .capture = { + .stream_name = "WCD AIF1 Capture", + .rates = WCD939X_RATES_MASK | WCD939X_FRAC_RATES_MASK, + .formats = WCD939X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd939x_sdw_dai_ops, + }, +}; + +static int wcd939x_bind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + unsigned int version, id1, status1; + int ret; + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Get USBSS type-c switch to send gnd/mic swap events + * typec_switch is fetched now to avoid a probe deadlock since + * the USBSS depends on the typec_mux register in wcd939x_probe() + */ + if (wcd939x->typec_analog_mux) { + wcd939x->typec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(wcd939x->typec_switch)) + return dev_err_probe(dev, PTR_ERR(wcd939x->typec_switch), + "failed to acquire orientation-switch\n"); + } +#endif /* CONFIG_TYPEC */ + + ret = component_bind_all(dev, wcd939x); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + goto err_put_typec_switch; + } + + wcd939x->rxdev = wcd939x_sdw_device_get(wcd939x->rxnode); + if (!wcd939x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + ret = -EINVAL; + goto err_unbind; + } + wcd939x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd939x->rxdev); + wcd939x->sdw_priv[AIF1_PB]->wcd939x = wcd939x; + + wcd939x->txdev = wcd939x_sdw_device_get(wcd939x->txnode); + if (!wcd939x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + ret = -EINVAL; + goto err_put_rxdev; + } + wcd939x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd939x->txdev); + wcd939x->sdw_priv[AIF1_CAP]->wcd939x = wcd939x; + wcd939x->tx_sdw_dev = dev_to_sdw_dev(wcd939x->txdev); + + /* + * As TX is main CSR reg interface, which should not be suspended first. + * explicitly add the dependency link + */ + if (!device_link_add(wcd939x->rxdev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink tx and rx\n"); + ret = -EINVAL; + goto err_put_txdev; + } + + if (!device_link_add(dev, wcd939x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and tx\n"); + ret = -EINVAL; + goto err_remove_rxtx_link; + } + + if (!device_link_add(dev, wcd939x->rxdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and rx\n"); + ret = -EINVAL; + goto err_remove_tx_link; + } + + /* Get regmap from TX SoundWire device */ + wcd939x->regmap = wcd939x_swr_get_regmap(wcd939x->sdw_priv[AIF1_CAP]); + if (IS_ERR(wcd939x->regmap)) { + dev_err(dev, "could not get TX device regmap\n"); + ret = PTR_ERR(wcd939x->regmap); + goto err_remove_rx_link; + } + + ret = wcd939x_irq_init(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret); + goto err_remove_rx_link; + } + + wcd939x->sdw_priv[AIF1_PB]->slave_irq = wcd939x->virq; + wcd939x->sdw_priv[AIF1_CAP]->slave_irq = wcd939x->virq; + + ret = wcd939x_set_micbias_data(wcd939x); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_remove_rx_link; + } + + /* Check WCD9395 version */ + 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); + else + version = WCD939X_VERSION_2_0; + + dev_dbg(dev, "wcd939x version: %s\n", version_to_str(version)); + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd939x, + wcd939x_dais, ARRAY_SIZE(wcd939x_dais)); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_remove_rx_link; + } + + return 0; + +err_remove_rx_link: + device_link_remove(dev, wcd939x->rxdev); +err_remove_tx_link: + device_link_remove(dev, wcd939x->txdev); +err_remove_rxtx_link: + device_link_remove(wcd939x->rxdev, wcd939x->txdev); +err_put_txdev: + put_device(wcd939x->txdev); +err_put_rxdev: + put_device(wcd939x->rxdev); +err_unbind: + component_unbind_all(dev, wcd939x); +err_put_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_put(wcd939x->typec_switch); +#endif /* CONFIG_TYPEC */ + + return ret; +} + +static void wcd939x_unbind(struct device *dev) +{ + struct wcd939x_priv *wcd939x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd939x->txdev); + device_link_remove(dev, wcd939x->rxdev); + device_link_remove(wcd939x->rxdev, wcd939x->txdev); + put_device(wcd939x->txdev); + put_device(wcd939x->rxdev); + component_unbind_all(dev, wcd939x); +} + +static const struct component_master_ops wcd939x_comp_ops = { + .bind = wcd939x_bind, + .unbind = wcd939x_unbind, +}; + +static int wcd939x_add_slave_components(struct wcd939x_priv *wcd939x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np = dev->of_node; + + wcd939x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd939x->rxnode) { + dev_err(dev, "%s: Rx-device node not defined\n", __func__); + return -ENODEV; + } + + of_node_get(wcd939x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->rxnode); + + wcd939x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd939x->txnode) { + dev_err(dev, "%s: Tx-device node not defined\n", __func__); + return -ENODEV; + } + of_node_get(wcd939x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd939x->txnode); + return 0; +} + +static int wcd939x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct wcd939x_priv *wcd939x = NULL; + struct device *dev = &pdev->dev; + int ret; + + wcd939x = devm_kzalloc(dev, sizeof(struct wcd939x_priv), + GFP_KERNEL); + if (!wcd939x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd939x); + mutex_init(&wcd939x->micb_lock); + + ret = wcd939x_populate_dt_data(wcd939x, dev); + if (ret) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + return -EINVAL; + } + +#if IS_ENABLED(CONFIG_TYPEC) + /* + * Is USBSS is used to mux analog lines, + * register a typec mux/switch to get typec events + */ + if (wcd939x->typec_analog_mux) { + struct typec_mux_desc mux_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_mux_set, + }; + struct typec_switch_desc sw_desc = { + .drvdata = wcd939x, + .fwnode = dev_fwnode(dev), + .set = wcd939x_typec_switch_set, + }; + + wcd939x->typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(wcd939x->typec_mux)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_mux), + "failed to register typec mux\n"); + goto err_disable_regulators; + } + + wcd939x->typec_sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(wcd939x->typec_sw)) { + ret = dev_err_probe(dev, PTR_ERR(wcd939x->typec_sw), + "failed to register typec switch\n"); + goto err_unregister_typec_mux; + } + } +#endif /* CONFIG_TYPEC */ + + ret = wcd939x_add_slave_components(wcd939x, dev, &match); + if (ret) + goto err_unregister_typec_switch; + + wcd939x_reset(wcd939x); + + ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match); + if (ret) + goto err_disable_regulators; + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +#if IS_ENABLED(CONFIG_TYPEC) +err_unregister_typec_mux: + if (wcd939x->typec_analog_mux) + typec_mux_unregister(wcd939x->typec_mux); +#endif /* CONFIG_TYPEC */ + +err_unregister_typec_switch: +#if IS_ENABLED(CONFIG_TYPEC) + if (wcd939x->typec_analog_mux) + typec_switch_unregister(wcd939x->typec_sw); +#endif /* CONFIG_TYPEC */ + +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) +static const struct of_device_id wcd939x_dt_match[] = { + { .compatible = "qcom,wcd9390-codec" }, + { .compatible = "qcom,wcd9395-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, wcd939x_dt_match); +#endif + +static struct platform_driver wcd939x_codec_driver = { + .probe = wcd939x_probe, + .remove_new = wcd939x_remove, + .driver = { + .name = "wcd939x_codec", + .of_match_table = of_match_ptr(wcd939x_dt_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd939x_codec_driver); +MODULE_DESCRIPTION("WCD939X Codec driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f0f1021fc9cb88ebdc241b6121107399ee4f2eb7 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:19 +0200 Subject: ASoC: amd: acp: Drop redundant initialization of machine driver data Simplify driver data configuration by removing redundant initialization of members in static structs. Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-2-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sof-mach.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 2a9fd3275e42..1d313fcb5f2d 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -28,7 +28,6 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -38,7 +37,6 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_rt1019_data = { @@ -48,7 +46,6 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_max_data = { @@ -58,7 +55,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .tdm_mode = false, }; static struct acp_card_drvdata sof_nau8825_data = { @@ -69,7 +65,6 @@ static struct acp_card_drvdata sof_nau8825_data = { .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, .soc_mclk = true, - .tdm_mode = false, }; static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { @@ -80,20 +75,15 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .amp_codec_id = RT1019, .dmic_codec_id = DMIC, .soc_mclk = true, - .tdm_mode = false, }; static struct acp_card_drvdata sof_nau8821_max98388_data = { .hs_cpu_id = I2S_SP, .amp_cpu_id = I2S_HS, .bt_cpu_id = I2S_BT, - .dmic_cpu_id = NONE, .hs_codec_id = NAU8821, .amp_codec_id = MAX98388, - .bt_codec_id = NONE, - .dmic_codec_id = NONE, .soc_mclk = true, - .tdm_mode = false, }; static int acp_sof_probe(struct platform_device *pdev) -- cgit v1.2.3 From 68ab29426d88294d16170919a6a6e764f375113f Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:20 +0200 Subject: ASoC: amd: acp: Make use of existing *_CODEC_DAI macros The generic ACP machine driver provides macros for NAU88221 and MAX98388 codec DAI names, but in places it is still using directly the related strings. For consistency, replace all strings with the equivalent macros. Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-3-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-mach-common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index c90ec3419247..346f7514c81a 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -821,8 +821,8 @@ static const struct snd_soc_ops acp_card_maxim_ops = { }; SND_SOC_DAILINK_DEF(max98388, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"), - COMP_CODEC("i2c-ADS8388:01", "max98388-aif1"))); + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI), + COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI))); static const struct snd_kcontrol_new max98388_controls[] = { SOC_DAPM_PIN_SWITCH("Left Spk"), @@ -1273,7 +1273,7 @@ static const struct snd_soc_ops acp_8821_ops = { SND_SOC_DAILINK_DEF(nau8821, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00", - "nau8821-hifi"))); + NAU8821_CODEC_DAI))); /* Declare DMIC codec components */ SND_SOC_DAILINK_DEF(dmic_codec, -- cgit v1.2.3 From d0ada20279db2649a7549a2b8a4a3379c59f238d Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:21 +0200 Subject: ASoC: amd: acp: Add missing error handling in sof-mach Handle potential acp_sofdsp_dai_links_create() errors in ACP SOF machine driver's probe function. Note there is no need for an undo. While at it, switch to dev_err_probe(). Fixes: 9f84940f5004 ("ASoC: amd: acp: Add SOF audio support on Chrome board") Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-4-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sof-mach.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 1d313fcb5f2d..6f0ca23638af 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -112,16 +112,14 @@ static int acp_sof_probe(struct platform_device *pdev) if (dmi_id && dmi_id->driver_data) acp_card_drvdata->tdm_mode = dmi_id->driver_data; - acp_sofdsp_dai_links_create(card); + ret = acp_sofdsp_dai_links_create(card); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n"); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, - "devm_snd_soc_register_card(%s) failed: %d\n", - card->name, ret); - return ret; - } - + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register card(%s)\n", card->name); return 0; } -- cgit v1.2.3 From a4832a94688000662d4ebb8a1c05f086a9c98826 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:22 +0200 Subject: ASoC: amd: acp: Update MODULE_DESCRIPTION for sof-mach The current MODULE_DESCRIPTION relates to a Chrome board, as that was what the driver initially supported. Nonetheless, it has since progressed incrementally and evolved into a more comprehensive machine driver. Hence, update MODULE_DESCRIPTION to better reflect this. Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-5-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-sof-mach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 6f0ca23638af..19ff4fe5b1ea 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -166,7 +166,7 @@ static struct platform_driver acp_asoc_audio = { module_platform_driver(acp_asoc_audio); MODULE_IMPORT_NS(SND_SOC_AMD_MACH); -MODULE_DESCRIPTION("ACP chrome SOF audio support"); +MODULE_DESCRIPTION("ACP SOF Machine Driver"); MODULE_ALIAS("platform:rt5682-rt1019"); MODULE_ALIAS("platform:rt5682-max"); MODULE_ALIAS("platform:rt5682s-max"); -- cgit v1.2.3 From 222be59e5eed1554119294edc743ee548c2371d0 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:23 +0200 Subject: ASoC: SOF: amd: Fix memory leak in amd_sof_acp_probe() Driver uses kasprintf() to initialize fw_{code,data}_bin members of struct acp_dev_data, but kfree() is never called to deallocate the memory, which results in a memory leak. Fix the issue by switching to devm_kasprintf(). Additionally, ensure the allocation was successful by checking the pointer validity. Fixes: f7da88003c53 ("ASoC: SOF: amd: Enable signed firmware image loading for Vangogh platform") Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-6-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 32a741fcb84f..9c56d8adf8c5 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -561,17 +561,27 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) adata->signed_fw_image = false; dmi_id = dmi_first_match(acp_sof_quirk_table); if (dmi_id && dmi_id->driver_data) { - adata->fw_code_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-code.bin", - plat_data->fw_filename_prefix, - chip->name); - adata->fw_data_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-data.bin", - plat_data->fw_filename_prefix, - chip->name); - adata->signed_fw_image = dmi_id->driver_data; + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s/sof-%s-code.bin", + plat_data->fw_filename_prefix, + chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "%s/sof-%s-data.bin", + plat_data->fw_filename_prefix, + chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } - dev_dbg(sdev->dev, "fw_code_bin:%s, fw_data_bin:%s\n", adata->fw_code_bin, - adata->fw_data_bin); + adata->signed_fw_image = dmi_id->driver_data; } + adata->enable_fw_debug = enable_fw_debug; acp_memory_init(sdev); -- cgit v1.2.3 From a13f0c3c0e8fb3e61fbfd99c6b350cf9be0c4660 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:24 +0200 Subject: ASoC: SOF: amd: Optimize quirk for Valve Galileo Valve's Steam Deck OLED is uniquely identified by vendor and product name (Galileo) DMI fields. Simplify the quirk by removing the unnecessary match on product family. Additionally, fix the related comment as it points to the old product variant. Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-7-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 9c56d8adf8c5..dd33d24b3962 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -28,11 +28,10 @@ MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); const struct dmi_system_id acp_sof_quirk_table[] = { { - /* Valve Jupiter device */ + /* Steam Deck OLED device */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Valve"), DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), }, .driver_data = (void *)SECURED_FIRMWARE, }, -- cgit v1.2.3 From 369b997a1371aeecd0a1fb0f9f4ef3747a1d07a4 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:25 +0200 Subject: ASoC: SOF: core: Skip firmware test for custom loaders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACP driver for Vangogh platform uses a quirk for Valve Galileo device to setup a custom firmware loader, which neither requires nor uses the firmware file indicated via the default_fw_filename member of struct sof_dev_desc. Since commit 6c393ebbd74a ("ASoC: SOF: core: Implement IPC version fallback if firmware files are missing"), the provided filename gets verified and triggers a fatal error on probe: [ 7.719337] snd_sof_amd_vangogh 0000:04:00.5: enabling device (0000 -> 0002) [ 7.721486] snd_sof_amd_vangogh 0000:04:00.5: SOF firmware and/or topology file not found. [ 7.721565] snd_sof_amd_vangogh 0000:04:00.5: Supported default profiles [ 7.721569] snd_sof_amd_vangogh 0000:04:00.5: - ipc type 0 (Requested): [ 7.721573] snd_sof_amd_vangogh 0000:04:00.5: Firmware file: amd/sof/sof-vangogh.ri [ 7.721577] snd_sof_amd_vangogh 0000:04:00.5: Topology file: amd/sof-tplg/sof-vangogh-nau8821-max.tplg [ 7.721582] snd_sof_amd_vangogh 0000:04:00.5: Check if you have 'sof-firmware' package installed. [ 7.721585] snd_sof_amd_vangogh 0000:04:00.5: Optionally it can be manually downloaded from: [ 7.721589] snd_sof_amd_vangogh 0000:04:00.5: https://github.com/thesofproject/sof-bin/ [ 7.721997] snd_sof_amd_vangogh: probe of 0000:04:00.5 failed with error -2 According to AMD, a combined ".ri" file which includes the code and data segments cannot be used due to a limitation preventing the code image to be signed on build time. Fix the issue by skipping the firmware file test if a custom loader is being used instead of the generic one. Fixes: 6c393ebbd74a ("ASoC: SOF: core: Implement IPC version fallback if firmware files are missing") Co-developed-by: Péter Ujfalusi Signed-off-by: Péter Ujfalusi Signed-off-by: Cristian Ciocaltea Link: https://msgid.link/r/20231219030728.2431640-8-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/fw-file-profile.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/fw-file-profile.c b/sound/soc/sof/fw-file-profile.c index 138a1ca2c4a8..b56b14232444 100644 --- a/sound/soc/sof/fw-file-profile.c +++ b/sound/soc/sof/fw-file-profile.c @@ -89,6 +89,12 @@ static int sof_test_topology_file(struct device *dev, return ret; } +static bool sof_platform_uses_generic_loader(struct snd_sof_dev *sdev) +{ + return (sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_raw || + sdev->pdata->desc->ops->load_firmware == snd_sof_load_firmware_memcpy); +} + static int sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, enum sof_ipc_type ipc_type, @@ -130,7 +136,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, * For default path and firmware name do a verification before * continuing further. */ - if (base_profile->fw_path || base_profile->fw_name) { + if ((base_profile->fw_path || base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) { ret = sof_test_firmware_file(dev, out_profile, &ipc_type); if (ret) return ret; @@ -181,7 +188,8 @@ sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev, out_profile->ipc_type = ipc_type; /* Test only default firmware file */ - if (!base_profile->fw_path && !base_profile->fw_name) + if ((!base_profile->fw_path && !base_profile->fw_name) && + sof_platform_uses_generic_loader(sdev)) ret = sof_test_firmware_file(dev, out_profile, NULL); if (!ret) @@ -267,7 +275,11 @@ static void sof_print_profile_info(struct snd_sof_dev *sdev, dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type); - dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name); + /* The firmware path is only valid when generic loader is used */ + if (sof_platform_uses_generic_loader(sdev)) + dev_info(dev, " Firmware file: %s/%s\n", + profile->fw_path, profile->fw_name); + if (profile->fw_lib_path) dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path); dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name); -- cgit v1.2.3 From d9cacc1a2af2e1cd781b5cd2a3e57fbde64f5a2d Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 19 Dec 2023 05:07:26 +0200 Subject: ASoC: SOF: amd: Compute file paths on firmware load Commit 6c393ebbd74a ("ASoC: SOF: core: Implement IPC version fallback if firmware files are missing") changed the order of some operations and the firmware paths are not available anymore at snd_sof_probe() time. Precisely, fw_filename_prefix is set by sof_select_ipc_and_paths() via plat_data->fw_filename_prefix = out_profile.fw_path; but sof_init_environment() which calls this function was moved from snd_sof_device_probe() to sof_probe_continue(). Moreover, snd_sof_probe() was moved from sof_probe_continue() to sof_init_environment(), but before the call to sof_select_ipc_and_paths(). The problem here is that amd_sof_acp_probe() uses fw_filename_prefix to compute fw_code_bin and fw_data_bin paths, and because the field is not yet initialized, the paths end up containing (null): snd_sof_amd_vangogh 0000:04:00.5: Direct firmware load for (null)/sof-vangogh-code.bin failed with error -2 snd_sof_amd_vangogh 0000:04:00.5: sof signed firmware code bin is missing snd_sof_amd_vangogh 0000:04:00.5: error: failed to load DSP firmware -2 snd_sof_amd_vangogh: probe of 0000:04:00.5 failed with error -2 Move usage of fw_filename_prefix right before request_firmware() calls in acp_sof_load_signed_firmware(). Fixes: 6c393ebbd74a ("ASoC: SOF: core: Implement IPC version fallback if firmware files are missing") Signed-off-by: Cristian Ciocaltea Reviewed-by: Emil Velikov Link: https://msgid.link/r/20231219030728.2431640-9-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 32 ++++++++++++++++++++++++++------ sound/soc/sof/amd/acp.c | 7 ++----- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index e05eb7a86dd4..d2d21478399e 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -267,29 +267,49 @@ int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata = plat_data->hw_pdata; + const char *fw_filename; int ret; - ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev); + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_code_bin); + if (!fw_filename) + return -ENOMEM; + + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware code bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0, - (void *)sdev->basefw.fw->data, sdev->basefw.fw->size); + (void *)sdev->basefw.fw->data, + sdev->basefw.fw->size); + + fw_filename = kasprintf(GFP_KERNEL, "%s/%s", + plat_data->fw_filename_prefix, + adata->fw_data_bin); + if (!fw_filename) + return -ENOMEM; - ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev); + ret = request_firmware(&adata->fw_dbin, fw_filename, sdev->dev); if (ret < 0) { + kfree(fw_filename); dev_err(sdev->dev, "sof signed firmware data bin is missing\n"); return ret; } else { - dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin); + dev_dbg(sdev->dev, "request_firmware %s successful\n", fw_filename); } + kfree(fw_filename); ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, - (void *)adata->fw_dbin->data, adata->fw_dbin->size); + (void *)adata->fw_dbin->data, + adata->fw_dbin->size); return ret; } EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index dd33d24b3962..a2b441e3d6d3 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -493,7 +493,6 @@ EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); - struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; const struct sof_amd_acp_desc *chip; const struct dmi_system_id *dmi_id; @@ -561,8 +560,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) dmi_id = dmi_first_match(acp_sof_quirk_table); if (dmi_id && dmi_id->driver_data) { adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "%s/sof-%s-code.bin", - plat_data->fw_filename_prefix, + "sof-%s-code.bin", chip->name); if (!adata->fw_code_bin) { ret = -ENOMEM; @@ -570,8 +568,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) } adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "%s/sof-%s-data.bin", - plat_data->fw_filename_prefix, + "sof-%s-data.bin", chip->name); if (!adata->fw_data_bin) { ret = -ENOMEM; -- cgit v1.2.3 From 322ed3a10bf2dc85568aa9ed285aba448347080c Mon Sep 17 00:00:00 2001 From: Erick Archer Date: Sat, 6 Jan 2024 18:16:35 +0100 Subject: ASoC: qcom: Use devm_kcalloc() instead of devm_kzalloc() Use 2-factor multiplication argument form devm_kcalloc() instead of devm_kzalloc(). Link: https://github.com/KSPP/linux/issues/162 Signed-off-by: Erick Archer Reviewed-by: Gustavo A. R. Silva Link: https://msgid.link/r/20240106171635.19881-1-erick.archer@gmx.com Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 756706d5b493..747041fa7866 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -73,7 +73,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) link = card->dai_link; for_each_available_child_of_node(dev->of_node, np) { - dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); + dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL); if (!dlc) { ret = -ENOMEM; goto err_put_np; -- cgit v1.2.3 From 90050b8d2e1556238d4c69abc11270de523bf955 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 10 Jan 2024 20:57:36 -0800 Subject: ASoC: p1022_rdk: fix all kernel-doc warnings Fix several kernel-doc warnings in p1022_rdk.c: p1022_rdk.c:70: warning: cannot understand function prototype: 'struct machine_data ' p1022_rdk.c:90: warning: Function parameter or struct member 'card' not described in 'p1022_rdk_machine_probe' p1022_rdk.c:90: warning: No description found for return value of 'p1022_rdk_machine_probe' p1022_rdk.c:129: warning: Function parameter or struct member 'substream' not described in 'p1022_rdk_startup' p1022_rdk.c:129: warning: No description found for return value of 'p1022_rdk_startup' p1022_rdk.c:162: warning: Function parameter or struct member 'card' not described in 'p1022_rdk_machine_remove' p1022_rdk.c:162: warning: No description found for return value of 'p1022_rdk_machine_remove' p1022_rdk.c:187: warning: cannot understand function prototype: 'const struct snd_soc_ops p1022_rdk_ops = ' p1022_rdk.c:199: warning: Function parameter or struct member 'pdev' not described in 'p1022_rdk_probe' p1022_rdk.c:199: warning: No description found for return value of 'p1022_rdk_probe' p1022_rdk.c:349: warning: Function parameter or struct member 'pdev' not described in 'p1022_rdk_remove' p1022_rdk.c:376: warning: No description found for return value of 'p1022_rdk_init' Signed-off-by: Randy Dunlap Cc: Liam Girdwood Cc: Mark Brown Cc: Cc: Jaroslav Kysela Cc: Takashi Iwai Link: https://msgid.link/r/20240111045736.7500-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/p1022_rdk.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c index 18d129c21648..a42149311c8b 100644 --- a/sound/soc/fsl/p1022_rdk.c +++ b/sound/soc/fsl/p1022_rdk.c @@ -61,7 +61,7 @@ static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts, /* There's only one global utilities register */ static phys_addr_t guts_phys; -/** +/* * machine_data: machine-specific ASoC device data * * This structure contains data for a single sound platform device on an @@ -80,11 +80,14 @@ struct machine_data { }; /** - * p1022_rdk_machine_probe: initialize the board + * p1022_rdk_machine_probe - initialize the board + * @card: ASoC card instance * * This function is used to initialize the board-specific hardware. * * Here we program the DMACR and PMUXCR registers. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_machine_probe(struct snd_soc_card *card) { @@ -119,11 +122,14 @@ static int p1022_rdk_machine_probe(struct snd_soc_card *card) } /** - * p1022_rdk_startup: program the board with various hardware parameters + * p1022_rdk_startup - program the board with various hardware parameters + * @substream: ASoC substream object * * This function takes board-specific information, like clock frequencies * and serial data formats, and passes that information to the codec and * transport drivers. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_startup(struct snd_pcm_substream *substream) { @@ -153,10 +159,13 @@ static int p1022_rdk_startup(struct snd_pcm_substream *substream) } /** - * p1022_rdk_machine_remove: Remove the sound device + * p1022_rdk_machine_remove - Remove the sound device + * @card: ASoC card instance * * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_machine_remove(struct snd_soc_card *card) { @@ -181,7 +190,7 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card) return 0; } -/** +/* * p1022_rdk_ops: ASoC machine driver operations */ static const struct snd_soc_ops p1022_rdk_ops = { @@ -189,11 +198,14 @@ static const struct snd_soc_ops p1022_rdk_ops = { }; /** - * p1022_rdk_probe: platform probe function for the machine driver + * p1022_rdk_probe - platform probe function for the machine driver + * @pdev: platform device pointer * * Although this is a machine driver, the SSI node is the "master" node with * respect to audio hardware connections. Therefore, we create a new ASoC * device for each new SSI node that has a codec attached. + * + * Returns: %0 on success or negative errno value on error */ static int p1022_rdk_probe(struct platform_device *pdev) { @@ -341,7 +353,8 @@ error_put: } /** - * p1022_rdk_remove: remove the platform device + * p1022_rdk_remove - remove the platform device + * @pdev: platform device pointer * * This function is called when the platform device is removed. */ @@ -368,9 +381,11 @@ static struct platform_driver p1022_rdk_driver = { }; /** - * p1022_rdk_init: machine driver initialization. + * p1022_rdk_init - machine driver initialization. * * This function is called when this module is loaded. + * + * Returns: %0 on success or negative errno value on error */ static int __init p1022_rdk_init(void) { @@ -391,7 +406,7 @@ static int __init p1022_rdk_init(void) } /** - * p1022_rdk_exit: machine driver exit + * p1022_rdk_exit - machine driver exit * * This function is called when this driver is unloaded. */ -- cgit v1.2.3 From 9423d7b9edba043c39f1607c752677c8b769922f Mon Sep 17 00:00:00 2001 From: David Lin Date: Tue, 16 Jan 2024 10:45:20 +0800 Subject: ASoC: nau8540: Add pre-charge actions for input Adding pre-charge mechanism to make FEPGA power stable faster. It not only improved the recording quality at the beginning but also meaningfully decreased the final adc delay time. Signed-off-by: David Lin Link: https://msgid.link/r/20240116024519.24569-1-CTLIN0@nuvoton.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8540.c | 116 +++++++++++++++++++++++++++++++-------------- sound/soc/codecs/nau8540.h | 13 ++++- 2 files changed, 92 insertions(+), 37 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index f66417a0f29f..22251fb2fa1f 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -26,7 +26,6 @@ #include #include "nau8540.h" - #define NAU_FREF_MAX 13500000 #define NAU_FVCO_MAX 100000000 #define NAU_FVCO_MIN 90000000 @@ -230,6 +229,49 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new digital_ch1_mux = SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); +static int nau8540_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF | + NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF | + NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF | + NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF | + NAU8540_ACDC_CTL_MIC4N_VREF); + break; + default: + break; + } + return 0; +} + +static int nau8540_precharge_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE, + NAU8540_DISCHRG_EN, 0); + regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2, + NAU8540_ACDC_CTL_MASK, 0); + break; + default: + break; + } + return 0; +} + static int adc_power_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -237,8 +279,10 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); if (SND_SOC_DAPM_EVENT_ON(event)) { - msleep(300); + msleep(160); /* DO12 and DO34 pad output enable */ + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, NAU8540_I2S_DO12_TRI, 0); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, @@ -248,6 +292,8 @@ static int adc_power_control(struct snd_soc_dapm_widget *w, NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); + regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT, + NAU8540_ADC_ALL_EN, 0); } return 0; } @@ -274,28 +320,26 @@ static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC3"), SND_SOC_DAPM_INPUT("MIC4"), - SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), - SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), - - SND_SOC_DAPM_ADC_E("ADC1", NULL, - NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC2", NULL, - NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC3", NULL, - NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_ADC_E("ADC4", NULL, - NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0, + nau8540_fepga_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0, + nau8540_precharge_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0, + adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_MUX("Digital CH4 Mux", SND_SOC_NOPM, 0, 0, &digital_ch4_mux), @@ -316,20 +360,20 @@ static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { {"Frontend PGA3", NULL, "MIC3"}, {"Frontend PGA4", NULL, "MIC4"}, - {"ADC1", NULL, "Frontend PGA1"}, - {"ADC2", NULL, "Frontend PGA2"}, - {"ADC3", NULL, "Frontend PGA3"}, - {"ADC4", NULL, "Frontend PGA4"}, + {"Precharge", NULL, "Frontend PGA1"}, + {"Precharge", NULL, "Frontend PGA2"}, + {"Precharge", NULL, "Frontend PGA3"}, + {"Precharge", NULL, "Frontend PGA4"}, - {"ADC CH1", NULL, "ADC1"}, - {"ADC CH2", NULL, "ADC2"}, - {"ADC CH3", NULL, "ADC3"}, - {"ADC CH4", NULL, "ADC4"}, + {"ADC CH1", NULL, "Precharge"}, + {"ADC CH2", NULL, "Precharge"}, + {"ADC CH3", NULL, "Precharge"}, + {"ADC CH4", NULL, "Precharge"}, - {"ADC1", NULL, "MICBIAS1"}, - {"ADC2", NULL, "MICBIAS1"}, - {"ADC3", NULL, "MICBIAS2"}, - {"ADC4", NULL, "MICBIAS2"}, + {"ADC CH1", NULL, "MICBIAS1"}, + {"ADC CH2", NULL, "MICBIAS1"}, + {"ADC CH3", NULL, "MICBIAS2"}, + {"ADC CH4", NULL, "MICBIAS2"}, {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"}, {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"}, diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h index 2ce6063d462b..762bb93b06fd 100644 --- a/sound/soc/codecs/nau8540.h +++ b/sound/soc/codecs/nau8540.h @@ -78,6 +78,7 @@ /* POWER_MANAGEMENT (0x01) */ +#define NAU8540_ADC_ALL_EN 0xf #define NAU8540_ADC4_EN (0x1 << 3) #define NAU8540_ADC3_EN (0x1 << 2) #define NAU8540_ADC2_EN (0x1 << 1) @@ -202,6 +203,7 @@ /* REFERENCE (0x68) */ #define NAU8540_PRECHARGE_DIS (0x1 << 13) #define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8540_DISCHRG_EN (0x1 << 11) /* FEPGA1 (0x69) */ #define NAU8540_FEPGA1_MODCH2_SHT_SFT 7 @@ -214,7 +216,16 @@ #define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT) #define NAU8540_FEPGA2_MODCH3_SHT_SFT 3 #define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT) - +#define NAU8540_ACDC_CTL_SFT 8 +#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT) +#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15) +#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14) +#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13) +#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12) +#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11) +#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10) +#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9) +#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8) /* System Clock Source */ enum { -- cgit v1.2.3 From be69eae9673638583cfcad44c1da6abf91efc4a3 Mon Sep 17 00:00:00 2001 From: Erick Archer Date: Tue, 9 Jan 2024 19:11:01 +0100 Subject: ASoC: ti: j721e-evm: Use devm_kcalloc() instead of devm_kzalloc() Use 2-factor multiplication argument form devm_kcalloc() instead of devm_kzalloc(). Link: https://github.com/KSPP/linux/issues/162 Reviewed-by: Gustavo A. R. Silva Acked-by: Peter Ujfalusi Signed-off-by: Erick Archer Link: https://msgid.link/r/20240109181101.3806-1-erick.archer@gmx.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index b4b158dc736e..d9d1e021f5b2 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -649,7 +649,7 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, * Link 2: McASP10 <- pcm3168a_1 ADC */ comp_count = 6; - compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), + compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent), GFP_KERNEL); if (!compnent) { ret = -ENOMEM; @@ -763,7 +763,7 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, * \ pcm3168a_b ADC */ comp_count = 8; - compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), + compnent = devm_kcalloc(priv->dev, comp_count, sizeof(*compnent), GFP_KERNEL); if (!compnent) { ret = -ENOMEM; -- cgit v1.2.3 From a9a0303dfe3fe2bc04512c4ce6a589131845d386 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 23 Jan 2024 17:56:13 +0100 Subject: ASoC: codecs: Add support for the framer codec The framer codec interacts with a framer. It allows to use some of the framer timeslots as audio channels to transport audio data over the framer E1/T1/J1 lines. It also reports line carrier detection events through the ALSA jack detection feature. Signed-off-by: Herve Codina Reviewed-by: Christophe Leroy Link: https://msgid.link/r/20240123165615.250303-2-herve.codina@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 15 ++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/framer-codec.c | 413 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 sound/soc/codecs/framer-codec.c (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1b21d2cc44d7..75d88bd1dc6f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -114,6 +114,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES8328_I2C imply SND_SOC_ES7134 imply SND_SOC_ES7241 + imply SND_SOC_FRAMER imply SND_SOC_GTM601 imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA @@ -1102,6 +1103,20 @@ config SND_SOC_ES8328_SPI depends on SPI_MASTER select SND_SOC_ES8328 +config SND_SOC_FRAMER + tristate "Framer codec" + depends on GENERIC_FRAMER + help + Enable support for the framer codec. + The framer codec uses the generic framer infrastructure to transport + some audio data over an analog E1/T1/J1 line. + This codec allows to use some of the time slots available on the TDM + bus on which the framer is connected to transport the audio data. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-framer. + + config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8217f2868f4e..4080646b2dd6 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -122,6 +122,7 @@ snd-soc-es8326-objs := es8326.o snd-soc-es8328-objs := es8328.o snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-framer-objs := framer-codec.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hda-objs := hdac_hda.o @@ -514,6 +515,7 @@ obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c new file mode 100644 index 000000000000..e5fcde9ee308 --- /dev/null +++ b/sound/soc/codecs/framer-codec.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Framer ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FRAMER_NB_CHANNEL 32 +#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT) + +struct framer_codec { + struct framer *framer; + struct device *dev; + struct snd_soc_jack jack; + struct notifier_block nb; + struct work_struct carrier_work; + int max_chan_playback; + int max_chan_capture; +}; + +static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + framer->max_chan_playback = hweight32(tx_mask); + if (framer->max_chan_playback > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n", + tx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + framer->max_chan_capture = hweight32(rx_mask); + if (framer->max_chan_capture > FRAMER_NB_CHANNEL) { + dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n", + rx_mask, FRAMER_NB_CHANNEL); + return -EINVAL; + } + + return 0; +} + +/* + * The constraints for format/channel is to match with the number of 8bit + * time-slots available. + */ +static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_pcm_format_t format = params_format(params); + struct snd_interval ch = {0}; + int width; + + width = snd_pcm_format_physical_width(format); + if (width == 8 || width == 16 || width == 32 || width == 64) { + ch.max = nb_ts * 8 / width; + } else { + dev_err(dai->dev, "format physical width %d not supported\n", width); + return -EINVAL; + } + + ch.min = ch.max ? 1 : 0; + + return snd_interval_refine(c, &ch); +} + +static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture); +} + +static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + unsigned int channels = params_channels(params); + unsigned int slot_width; + snd_pcm_format_t format; + struct snd_mask f_new; + + if (!channels || channels > nb_ts) { + dev_err(dai->dev, "channels %u not supported\n", nb_ts); + return -EINVAL; + } + + slot_width = (nb_ts / channels) * 8; + + snd_mask_none(&f_new); + pcm_for_each_format(format) { + if (snd_mask_test_format(f_old, format)) { + if (snd_pcm_format_physical_width(format) <= slot_width) + snd_mask_set_format(&f_new, format); + } + } + + return snd_mask_refine(f_old, &f_new); +} + +static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback); +} + +static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + + return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture); +} + +static u64 framer_formats(u8 nb_ts) +{ + unsigned int format_width; + unsigned int chan_width; + snd_pcm_format_t format; + u64 formats_mask; + + if (!nb_ts) + return 0; + + formats_mask = 0; + chan_width = nb_ts * 8; + pcm_for_each_format(format) { + /* Support physical width multiple of 8bit */ + format_width = snd_pcm_format_physical_width(format); + if (format_width == 0 || format_width % 8) + continue; + + /* + * And support physical width that can fit N times in the + * channel + */ + if (format_width > chan_width || chan_width % format_width) + continue; + + formats_mask |= pcm_format_to_bits(format); + } + return formats_mask; +} + +static int framer_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component); + snd_pcm_hw_rule_func_t hw_rule_channels_by_format; + snd_pcm_hw_rule_func_t hw_rule_format_by_channels; + unsigned int frame_bits; + u64 format; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + format = framer_formats(framer->max_chan_capture); + hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels; + frame_bits = framer->max_chan_capture * 8; + } else { + format = framer_formats(framer->max_chan_playback); + hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format; + hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels; + frame_bits = framer->max_chan_playback * 8; + } + + ret = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, format); + if (ret) { + dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels_by_format, dai, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) { + dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format_by_channels, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) { + dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + frame_bits); + if (ret < 0) { + dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + return 0; +} + +static u64 framer_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops framer_dai_ops = { + .startup = framer_dai_startup, + .set_tdm_slot = framer_dai_set_tdm_slot, + .auto_selectable_formats = framer_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats), +}; + +static struct snd_soc_dai_driver framer_dai_driver = { + .name = "framer", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = FRAMER_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .ops = &framer_dai_ops, +}; + +static void framer_carrier_work(struct work_struct *work) +{ + struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work); + struct framer_status framer_status; + int jack_status; + int ret; + + ret = framer_get_status(framer->framer, &framer_status); + if (ret) { + dev_err(framer->dev, "get framer status failed (%d)\n", ret); + return; + } + + jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0; + snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK); +} + +static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct framer_codec *framer = container_of(nb, struct framer_codec, nb); + + switch (action) { + case FRAMER_EVENT_STATUS: + queue_work(system_power_efficient_wq, &framer->carrier_work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int framer_component_probe(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + struct framer_status status; + char *name; + int ret; + + INIT_WORK(&framer->carrier_work, framer_carrier_work); + + name = "carrier"; + if (component->name_prefix) { + name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix); + if (!name) + return -ENOMEM; + } + + ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack); + if (component->name_prefix) + kfree(name); /* A copy is done by snd_soc_card_jack_new */ + if (ret) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + + ret = framer_init(framer->framer); + if (ret) { + dev_err(component->dev, "framer init failed (%d)\n", ret); + return ret; + } + + ret = framer_power_on(framer->framer); + if (ret) { + dev_err(component->dev, "framer power-on failed (%d)\n", ret); + goto framer_exit; + } + + /* Be sure that get_status is supported */ + ret = framer_get_status(framer->framer, &status); + if (ret) { + dev_err(component->dev, "get framer status failed (%d)\n", ret); + goto framer_power_off; + } + + framer->nb.notifier_call = framer_carrier_notifier; + ret = framer_notifier_register(framer->framer, &framer->nb); + if (ret) { + dev_err(component->dev, "Cannot register event notifier\n"); + goto framer_power_off; + } + + /* Queue work to set the initial value */ + queue_work(system_power_efficient_wq, &framer->carrier_work); + + return 0; + +framer_power_off: + framer_power_off(framer->framer); +framer_exit: + framer_exit(framer->framer); + return ret; +} + +static void framer_component_remove(struct snd_soc_component *component) +{ + struct framer_codec *framer = snd_soc_component_get_drvdata(component); + + framer_notifier_unregister(framer->framer, &framer->nb); + cancel_work_sync(&framer->carrier_work); + framer_power_off(framer->framer); + framer_exit(framer->framer); +} + +static const struct snd_soc_component_driver framer_component_driver = { + .probe = framer_component_probe, + .remove = framer_component_remove, + .endianness = 1, +}; + +static int framer_codec_probe(struct platform_device *pdev) +{ + struct framer_codec *framer; + + framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL); + if (!framer) + return -ENOMEM; + + framer->dev = &pdev->dev; + + /* Get framer from parents node */ + framer->framer = devm_framer_get(&pdev->dev, NULL); + if (IS_ERR(framer->framer)) + return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n"); + + platform_set_drvdata(pdev, framer); + + return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver, + &framer_dai_driver, 1); +} + +static struct platform_driver framer_codec_driver = { + .driver = { + .name = "framer-codec", + }, + .probe = framer_codec_probe, +}; +module_platform_driver(framer_codec_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("FRAMER ALSA SoC driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e7214441ca1562fbfb002200f46d7f83bbc2e621 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Wed, 24 Jan 2024 08:44:25 +0800 Subject: ASoC: codecs: Remove unneeded semicolon In the wcd939x codec driver, there are two instances where semicolons are used after closing braces of a switch-case statement. These semicolons are not required and do not adhere to the coding style guidelines. This patch removes the unnecessary semicolons at the end of the switch-case statements which cleans up the code and ensures consistency with the rest of the kernel coding style. Signed-off-by: Yang Li Link: https://msgid.link/r/20240124004425.54020-1-yang.lee@linux.alibaba.com Signed-off-by: Mark Brown --- sound/soc/codecs/wcd939x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 0ccc7b31d0c1..c49894aad8a5 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -970,7 +970,7 @@ static int wcd939x_codec_enable_dmic(struct snd_soc_dapm_widget *w, default: dev_err(component->dev, "%s: Invalid DMIC Selection\n", __func__); return -EINVAL; - }; + } switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1292,7 +1292,7 @@ static int wcd939x_micbias_control(struct snd_soc_component *component, dev_err(component->dev, "%s: Invalid micbias number: %d\n", __func__, micb_num); return -EINVAL; - }; + } switch (req) { case MICB_PULLUP_ENABLE: -- cgit v1.2.3 From 966323dd9a65dde599f59176280468a0cb04c875 Mon Sep 17 00:00:00 2001 From: Zhu Ning Date: Wed, 24 Jan 2024 14:48:06 +0800 Subject: ASoC: codecs: ES8326: Adding new volume kcontrols ES8326 features a headphone volume control register and four DAC volume control registers. We add new volume Kcontrols for these registers to enhance the configurability of the volume settings, providing users with greater flexibility. Signed-off-by: Zhu Ning Link: https://msgid.link/r/20240124064806.30511-2-zhuning0077@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/es8326.h | 5 ++- 2 files changed, 95 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index cbcd02ec6ba4..608862aebd71 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -36,6 +36,8 @@ struct es8326_priv { u8 jack_pol; u8 interrupt_src; u8 interrupt_clk; + u8 hpl_vol; + u8 hpr_vol; bool jd_inverted; unsigned int sysclk; @@ -121,6 +123,72 @@ static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol, return 0; } +static int es8326_hplvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpl_vol; + + return 0; +} + +static int es8326_hplvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpl_vol != hp_vol) { + es8326->hpl_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x70, (hp_vol << 4)); + return 1; + } + + return 0; +} + +static int es8326_hprvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8326->hpr_vol; + + return 0; +} + +static int es8326_hprvol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int hp_vol; + + hp_vol = ucontrol->value.integer.value[0]; + if (hp_vol > 5) + return -EINVAL; + if (es8326->hpr_vol != hp_vol) { + es8326->hpr_vol = hp_vol; + if (hp_vol >= 3) + hp_vol++; + regmap_update_bits(es8326->regmap, ES8326_HP_VOL, + 0x07, hp_vol); + return 1; + } + + return 0; +} + static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); @@ -151,15 +219,24 @@ static const char *const winsize[] = { static const char *const dacpol_txt[] = { "Normal", "R Invert", "L Invert", "L + R Invert" }; +static const char *const hp_spkvol_switch[] = { + "HPVOL: HPL+HPL, SPKVOL: HPL+HPL", + "HPVOL: HPL+HPR, SPKVOL: HPL+HPR", + "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR", + "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR", +}; + static const struct soc_enum dacpol = SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); static const struct soc_enum alc_winsize = SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); static const struct soc_enum drc_winsize = SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); +static const struct soc_enum hpvol_spkvol_switch = + SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch); static const struct snd_kcontrol_new es8326_snd_controls[] = { - SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), SOC_ENUM("Playback Polarity", dacpol), SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate), SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv), @@ -182,6 +259,17 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = { es8326_crosstalk1_get, es8326_crosstalk1_set), SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0, es8326_crosstalk2_get, es8326_crosstalk2_set), + SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hplvol_get, es8326_hplvol_set), + SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0, + es8326_hprvol_get, es8326_hprvol_set), + + SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv), + + SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch), }; static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { @@ -972,6 +1060,8 @@ static int es8326_resume(struct snd_soc_component *component) es8326->jack_remove_retry = 0; es8326->hp = 0; + es8326->hpl_vol = 0x03; + es8326->hpr_vol = 0x03; return 0; } diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 4234bbb900c4..ee12caef8105 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -69,7 +69,7 @@ #define ES8326_DAC_DSM 0x4D #define ES8326_DAC_RAMPRATE 0x4E #define ES8326_DAC_VPPSCALE 0x4F -#define ES8326_DAC_VOL 0x50 +#define ES8326_DACL_VOL 0x50 #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_DAC_CROSSTALK 0x55 @@ -81,6 +81,9 @@ #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C +#define ES8326_DACR_VOL 0xF4 +#define ES8326_SPKL_VOL 0xF5 +#define ES8326_SPKR_VOL 0xF6 #define ES8326_HP_MISC 0xF7 #define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9 -- cgit v1.2.3 From fb430b06397e5eebefd42584fe4dfabf2a3632e0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:11 +0000 Subject: ASoC: cs42l43: Tidy up header includes Use more forward declarations, move header guards to cover other includes, and rely less on including headers through other headers. Suggested-by: Andy Shevchenko Signed-off-by: Charles Keepax Reviewed-by: Andy Shevchenko Link: https://msgid.link/r/20240125103117.2622095-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 5 +++++ sound/soc/codecs/cs42l43-sdw.c | 1 + sound/soc/codecs/cs42l43.c | 8 ++++++++ sound/soc/codecs/cs42l43.h | 21 ++++++++++++--------- 4 files changed, 26 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 24a598f2ed9a..1d8d7bf0a6b0 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -6,19 +6,24 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include #include #include #include #include #include #include +#include #include #include +#include +#include #include #include #include #include #include +#include #include #include "cs42l43.h" diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c index 388f95853b69..60c00c05da05 100644 --- a/sound/soc/codecs/cs42l43-sdw.c +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 6a64681767de..f2332f90f833 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -6,17 +6,25 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include +#include #include #include #include #include +#include #include #include #include +#include #include +#include #include +#include #include +#include #include +#include #include #include #include diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 125e36861d5d..9924c13e1eb5 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -6,19 +6,14 @@ * Cirrus Logic International Semiconductor Ltd. */ -#include +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + #include -#include #include -#include -#include #include -#include +#include #include -#include - -#ifndef CS42L43_ASOC_INT_H -#define CS42L43_ASOC_INT_H #define CS42L43_INTERNAL_SYSCLK 24576000 #define CS42L43_DEFAULT_SLOTS 0x3F @@ -37,6 +32,14 @@ #define CS42L43_N_BUTTONS 6 +struct clk; +struct device; + +struct snd_soc_component; +struct snd_soc_jack; + +struct cs42l43; + struct cs42l43_codec { struct device *dev; struct cs42l43 *core; -- cgit v1.2.3 From 40f6281c1e7d733399bd42fe97a0aae00b967a91 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:12 +0000 Subject: ASoC: cs42l43: Minor code tidy ups Add some missing commas, refactor a couple small bits of code. Suggested-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 10 +++++----- sound/soc/codecs/cs42l43.c | 12 ++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 1d8d7bf0a6b0..4f7a405b7e06 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -29,11 +29,11 @@ #include "cs42l43.h" static const unsigned int cs42l43_accdet_us[] = { - 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000, }; static const unsigned int cs42l43_accdet_db_ms[] = { - 0, 125, 250, 500, 750, 1000, 1250, 1500 + 0, 125, 250, 500, 750, 1000, 1250, 1500, }; static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; @@ -851,6 +851,9 @@ static const char * const cs42l43_jack_text[] = { "Line-In", "Microphone", "Optical", }; +static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) == + ARRAY_SIZE(cs42l43_jack_text) - 1); + SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -873,9 +876,6 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int override = ucontrol->value.integer.value[0]; - BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != - ARRAY_SIZE(cs42l43_jack_text) - 1); - if (override >= e->items) return -EINVAL; diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index f2332f90f833..d418c0b0ce9a 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -1059,12 +1059,10 @@ static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); return ret; } @@ -1077,12 +1075,10 @@ static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, int ret; ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); - if (ret < 0) - return ret; + if (ret > 0) + ret = snd_soc_get_volsw(kcontrol, ucontrol); else if (!ret) ucontrol->value.integer.value[0] = ret; - else - ret = snd_soc_get_volsw(kcontrol, ucontrol); return ret; } -- cgit v1.2.3 From a2e7cf55db781654fdb2d3b2529e28c4d93e24fc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:13 +0000 Subject: ASoC: cs42l43: Check error from device_property_read_u32_array() Whilst reading cirrus,buttons-ohms the error from device_property_read_u32_array() is not checked, whilst there is a preceding device_property_count_u32() which is checked the property read can still fail. Add the missing check. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 4f7a405b7e06..67ccdc8bab6f 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -106,8 +106,13 @@ int cs42l43_set_jack(struct snd_soc_component *component, goto error; } - device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", - priv->buttons, ret); + ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n", + ret); + goto error; + } } else { priv->buttons[0] = 70; priv->buttons[1] = 185; -- cgit v1.2.3 From 7a93a9abe44386b4caa0e67977f41b8c9f06b51c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:14 +0000 Subject: ASoC: cs42l43: Add pm_ptr around the power ops Add missing pm_ptr around the power ops. Suggested-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index d418c0b0ce9a..1852cb072bd0 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2349,7 +2349,7 @@ MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); static struct platform_driver cs42l43_codec_driver = { .driver = { .name = "cs42l43-codec", - .pm = &cs42l43_codec_pm_ops, + .pm = pm_ptr(&cs42l43_codec_pm_ops), }, .probe = cs42l43_codec_probe, -- cgit v1.2.3 From 96c716887c1a918d4cb4610f5cf111280fda48f0 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:15 +0000 Subject: ASoC: cs42l43: Use USEC_PER_MSEC rather than hard coding Use USEC_PER_MSEC rather than the hard coded value of 1000. Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43-jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 67ccdc8bab6f..901b9dbcf585 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -647,7 +648,7 @@ static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) static int cs42l43_run_type_detect(struct cs42l43_codec *priv) { struct cs42l43 *cs42l43 = priv->core; - int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + int timeout_ms = ((2 * priv->detect_us) / USEC_PER_MSEC) + 200; unsigned int type = 0xff; unsigned long time_left; -- cgit v1.2.3 From fe04d1632cb4130fb47d18fe70ac292562a3b9c3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:16 +0000 Subject: ASoC: cs42l43: Refactor to use for_each_set_bit() Refactor the code in cs42l43_mask_to_slots() to use for_each_set_bit(). Suggested-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 1852cb072bd0..23e9557494af 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -6,10 +6,12 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include #include #include #include #include +#include #include #include #include @@ -547,23 +549,22 @@ static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned long mask, + int *slots, unsigned int nslots) { - int i; + int i = 0; + int slot; - for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { - int slot = ffs(mask) - 1; - - if (slot < 0) + for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) { + if (i == nslots) { + dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n", + mask); return; + } - slots[i] = slot; - - mask &= ~(1 << slot); + slots[i++] = slot; } - if (mask) - dev_warn(priv->dev, "Too many channels in TDM mask\n"); } static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -580,8 +581,10 @@ static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mas rx_mask = CS42L43_DEFAULT_SLOTS; } - cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); - cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); return 0; } @@ -2098,8 +2101,10 @@ static int cs42l43_component_probe(struct snd_soc_component *component) snd_soc_component_init_regmap(component, cs42l43->regmap); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); - cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots, + ARRAY_SIZE(priv->tx_slots)); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots, + ARRAY_SIZE(priv->rx_slots)); priv->component = component; priv->constraint = cs42l43_constraint; -- cgit v1.2.3 From 31c6e53a4da5fe626b99e1ebf777d751994e3281 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 25 Jan 2024 10:31:17 +0000 Subject: ASoC: cs42l43: Use fls to calculate the pre-divider for the PLL Use fls to calculate the pre-divider and input frequency for the PLL, this is marginally faster than the previous loop. Suggested-by: Andy Shevchenko Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240125103117.2622095-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 23e9557494af..2c402086924d 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -1338,10 +1338,9 @@ static int cs42l43_enable_pll(struct cs42l43_codec *priv) dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); - while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { - div++; - freq /= 2; - } + div = fls(freq) - + fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq); + freq >>= div; if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { int i; -- cgit v1.2.3 From 84b22af29ff6c74e09e3faa0ad52c843cca1f426 Mon Sep 17 00:00:00 2001 From: Maciej Strozek Date: Tue, 23 Jan 2024 11:32:46 +0000 Subject: ASoC: Intel: mtl-match: Add cs42l43_l0 cs35l56_l23 for MTL The layout is configured as: - Link0: CS42L43 Jack and mics (2ch) - Link2: 2x CS35L56 Speaker (amps 3 and 4, right) - Link3: 2x CS35L56 Speaker (amps 1 and 2, left) Corresponding SOF topology: https://github.com/thesofproject/sof/pull/8773 Signed-off-by: Maciej Strozek Link: https://msgid.link/r/20240123113246.75539-1-mstrozek@opensource.cirrus.com Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-mtl-match.c | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index feb12c6c85d1..23eaf47b3f24 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -377,6 +377,37 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = { + { + .adr = 0x00023201FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP3" + }, + { + .adr = 0x00023301FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP4" + } + +}; + +static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = { + { + .adr = 0x00033001fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00033101fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP2" + } +}; + static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = { /* Expected order: jack -> amp */ { @@ -554,6 +585,26 @@ static const struct snd_soc_acpi_link_adr mtl_cs42l43_cs35l56[] = { {} }; +static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] = { + /* Expected order: jack -> amp */ + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(cs35l56_2_r_adr), + .adr_d = cs35l56_2_r_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs35l56_3_l_adr), + .adr_d = cs35l56_3_l_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { /* mockup tests need to be first */ @@ -599,6 +650,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" }, + { + .link_mask = BIT(0) | BIT(2) | BIT(3), + .links = cs42l43_link0_cs35l56_link2_link3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", + }, { .link_mask = GENMASK(2, 0), .links = mtl_cs42l43_cs35l56, -- cgit v1.2.3 From 9a6d7c4fb2801b675a9c31a7ceb78c84b8c439bc Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 30 Jan 2024 15:08:22 +0000 Subject: ASoC: sh: rz-ssi: Fix error message print The devm_request_irq() call is done for "dma_rt" interrupt but the error message printed "dma_tx" interrupt on failure, fix this by updating dma_tx -> dma_rt in dev_err_probe() message. While at it aligned the code. Signed-off-by: Lad Prabhakar Fixes: 38c042b59af0248a ("ASoC: sh: rz-ssi: Update interrupt handling for half duplex channels") Reviewed-by: Geert Uytterhoeven Link: https://msgid.link/r/20240130150822.327434-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- sound/soc/sh/rz-ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 14cf1a41fb0d..9d103646973a 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1015,7 +1015,7 @@ static int rz_ssi_probe(struct platform_device *pdev) dev_name(&pdev->dev), ssi); if (ret < 0) return dev_err_probe(&pdev->dev, ret, - "irq request error (dma_tx)\n"); + "irq request error (dma_rt)\n"); } else { if (ssi->irq_tx < 0) return ssi->irq_tx; -- cgit v1.2.3 From ed0ef85795b58134172e8c82ab2f1b869cd501a6 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:35 +0530 Subject: ASoC/soundwire: implement generic api for scanning amd soundwire controller Implement generic function for scanning SoundWire controller. Same function will be used for legacy and sof stack for AMD platforms. Signed-off-by: Vijendar Mukunda Acked-by: Vinod Koul Link: https://msgid.link/r/20240129055147.1493853-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/linux/soundwire/sdw_amd.h | 15 ++++++++++ sound/soc/amd/acp/Kconfig | 7 +++++ sound/soc/amd/acp/Makefile | 2 ++ sound/soc/amd/acp/amd-sdw-acpi.c | 62 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 sound/soc/amd/acp/amd-sdw-acpi.c (limited to 'sound/soc') diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h index ceecad74aef9..41dd64941cef 100644 --- a/include/linux/soundwire/sdw_amd.h +++ b/include/linux/soundwire/sdw_amd.h @@ -6,6 +6,7 @@ #ifndef __SDW_AMD_H #define __SDW_AMD_H +#include #include /* AMD pm_runtime quirk definitions */ @@ -106,4 +107,18 @@ struct amd_sdw_manager { struct sdw_amd_dai_runtime **dai_runtime_array; }; + +/** + * struct sdw_amd_acpi_info - Soundwire AMD information found in ACPI tables + * @handle: ACPI controller handle + * @count: maximum no of soundwire manager links supported on AMD platform. + * @link_mask: bit-wise mask listing links enabled by BIOS menu + */ +struct sdw_amd_acpi_info { + acpi_handle handle; + int count; + u32 link_mask; +}; + +int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info); #endif diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 84c963241dc5..b3105ba9c3a3 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -101,6 +101,13 @@ config SND_SOC_AMD_MACH_COMMON help This option enables common Machine driver module for ACP. +config SND_AMD_SOUNDWIRE_ACPI + tristate "AMD SoundWire ACPI Support" + depends on ACPI + help + This options enables ACPI helper functions for SoundWire + interface for AMD platforms. + config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" depends on X86 && PCI && I2C diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index ff5f7893b81e..1fd581a2aa33 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -10,6 +10,7 @@ snd-acp-i2s-objs := acp-i2s.o snd-acp-pdm-objs := acp-pdm.o snd-acp-legacy-common-objs := acp-legacy-common.o snd-acp-pci-objs := acp-pci.o +snd-amd-sdw-acpi-objs := amd-sdw-acpi.o #platform specific driver snd-acp-renoir-objs := acp-renoir.o @@ -33,6 +34,7 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o +obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c new file mode 100644 index 000000000000..babd841d3296 --- /dev/null +++ b/sound/soc/amd/acp/amd-sdw-acpi.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Vijendar Mukunda + +/* + * SDW AMD ACPI scan helper function + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info) +{ + struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); + u32 sdw_bitmap = 0; + u8 count = 0; + int ret; + + if (!adev) + return -EINVAL; + + /* Found controller, find links supported */ + ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev), + "mipi-sdw-manager-list", &sdw_bitmap, 1); + if (ret) { + dev_err(&adev->dev, + "Failed to read mipi-sdw-manager-list: %d\n", ret); + return -EINVAL; + } + count = hweight32(sdw_bitmap); + /* Check count is within bounds */ + if (count > info->count) { + dev_err(&adev->dev, "Manager count %d exceeds max %d\n", + count, info->count); + return -EINVAL; + } + + if (!count) { + dev_dbg(&adev->dev, "No SoundWire Managers detected\n"); + return -EINVAL; + } + dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count); + info->link_mask = sdw_bitmap; + return 0; +} +EXPORT_SYMBOL_NS(amd_sdw_scan_controller, SND_AMD_SOUNDWIRE_ACPI); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("AMD SoundWire ACPI helpers"); -- cgit v1.2.3 From d948218424bf9194860fcc10259ff42487cf4bd9 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:41 +0530 Subject: ASoC: SOF: amd: add code for invoking soundwire manager helper functions Add code for invoking Soundwire manager helper functions when SoundWire configuration is selected. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-8-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 17 ++++++++ sound/soc/sof/amd/acp.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/sof/amd/acp.h | 15 ++++++- 3 files changed, 129 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 19c5e27a193f..1cea1d75130c 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -60,6 +60,23 @@ config SND_SOC_SOF_ACP_PROBES This option is not user-selectable but automatically handled by 'select' statements at a higher level +config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + tristate + select SOUNDWIRE_AMD if SND_SOC_SOF_AMD_SOUNDWIRE != n + select SND_AMD_SOUNDWIRE_ACPI if ACPI + +config SND_SOC_SOF_AMD_SOUNDWIRE + tristate "SOF support for SoundWire based AMD platforms" + default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE + depends on ACPI && SOUNDWIRE + depends on !(SOUNDWIRE=m && SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE=y) + help + This adds support for SoundWire with Sound Open Firmware + for AMD platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + config SND_SOC_SOF_AMD_ACP63 tristate "SOF support for ACP6.3 platform" depends on SND_SOC_SOF_PCI diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 32a741fcb84f..f24cd6b7490f 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -491,6 +491,81 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE) +static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr) +{ + struct acpi_device *sdw_dev; + struct acp_dev_data *acp_data; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + + if (!addr) + return -ENODEV; + + acp_data = sdev->pdata->hw_pdata; + sdw_dev = acpi_find_child_device(ACPI_COMPANION(sdev->dev), addr, 0); + if (!sdw_dev) + return -ENODEV; + + acp_data->info.handle = sdw_dev->handle; + acp_data->info.count = desc->sdw_max_link_count; + + return amd_sdw_scan_controller(&acp_data->info); +} + +static int amd_sof_sdw_probe(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + struct sdw_amd_res sdw_res; + int ret; + + acp_data = sdev->pdata->hw_pdata; + + memset(&sdw_res, 0, sizeof(sdw_res)); + sdw_res.addr = acp_data->addr; + sdw_res.reg_range = acp_data->reg_range; + sdw_res.handle = acp_data->info.handle; + sdw_res.parent = sdev->dev; + sdw_res.dev = sdev->dev; + sdw_res.acp_lock = &acp_data->acp_lock; + sdw_res.count = acp_data->info.count; + sdw_res.link_mask = acp_data->info.link_mask; + sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR]; + + ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); + if (ret) + dev_err(sdev->dev, "SoundWire probe failed\n"); + return ret; +} + +static int amd_sof_sdw_exit(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + + acp_data = sdev->pdata->hw_pdata; + if (acp_data->sdw) + sdw_amd_exit(acp_data->sdw); + acp_data->sdw = NULL; + + return 0; +} + +#else +static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr) +{ + return 0; +} + +static int amd_sof_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static int amd_sof_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} +#endif + int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); @@ -527,7 +602,9 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) } pci_set_master(pci); - + adata->addr = addr; + adata->reg_range = chip->reg_end_addr - chip->reg_start_addr; + mutex_init(&adata->acp_lock); sdev->pdata->hw_pdata = adata; adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL); if (!adata->smn_dev) { @@ -549,6 +626,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) if (ret < 0) goto free_ipc_irq; + /* scan SoundWire capabilities exposed by DSDT */ + ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr); + if (ret < 0) { + dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n"); + goto skip_soundwire; + } + ret = amd_sof_sdw_probe(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: SoundWire probe error\n"); + free_irq(sdev->ipc_irq, sdev); + pci_dev_put(adata->smn_dev); + return ret; + } + +skip_soundwire: sdev->dsp_box.offset = 0; sdev->dsp_box.size = BOX_SIZE_512; @@ -596,6 +688,9 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) if (adata->smn_dev) pci_dev_put(adata->smn_dev); + if (adata->sdw) + amd_sof_sdw_exit(sdev); + if (sdev->ipc_irq) free_irq(sdev->ipc_irq, sdev); @@ -607,4 +702,6 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON); MODULE_DESCRIPTION("AMD ACP sof driver"); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); +MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index c645aee216fd..e88d01330ec7 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -12,7 +12,7 @@ #define __SOF_AMD_ACP_H #include - +#include #include "../sof-priv.h" #include "../sof-audio.h" @@ -191,6 +191,10 @@ struct sof_amd_acp_desc { u32 acp_clkmux_sel; u32 fusion_dsp_offset; u32 probe_reg_offset; + u32 reg_start_addr; + u32 reg_end_addr; + u32 sdw_max_link_count; + u64 sdw_acpi_dev_addr; }; /* Common device data struct for ACP devices */ @@ -199,6 +203,12 @@ struct acp_dev_data { const struct firmware *fw_dbin; /* DMIC device */ struct platform_device *dmic_dev; + /* mutex lock to protect ACP common registers access */ + struct mutex acp_lock; + /* ACPI information stored between scan and probe steps */ + struct sdw_amd_acpi_info info; + /* sdw context allocated by SoundWire driver */ + struct sdw_amd_ctx *sdw; unsigned int fw_bin_size; unsigned int fw_data_bin_size; unsigned int fw_sram_data_bin_size; @@ -207,6 +217,9 @@ struct acp_dev_data { const char *fw_sram_data_bin; u32 fw_bin_page_count; u32 fw_data_bin_page_count; + u32 addr; + u32 reg_range; + u32 blk_type; dma_addr_t sha_dma_addr; u8 *bin_buf; dma_addr_t dma_addr; -- cgit v1.2.3 From 96eb818510120a869711876026ca7c0aa2b4171e Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:42 +0530 Subject: ASoC: SOF: amd: add interrupt handling for SoundWire manager devices Add support for interrupt handling for soundwire manager platform devices. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-9-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 4 ++++ sound/soc/sof/amd/acp.c | 38 +++++++++++++++++++++++++++++++++++++- sound/soc/sof/amd/acp.h | 5 +++++ 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index a913f1cc4c80..7ba6492b8e99 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -78,6 +78,10 @@ #define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 +/* ACP common registers to report errors related to I2S & SoundWire interfaces */ +#define ACP_SW0_I2S_ERROR_REASON 0x18B4 +#define ACP_SW1_I2S_ERROR_REASON 0x1A50 + /* Registers from ACP_SHA block */ #define ACP_SHA_DSP_FW_QUALIFIER 0x1C70 #define ACP_SHA_DMA_CMD 0x1CB0 diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index f24cd6b7490f..7a34faae9889 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -375,10 +375,13 @@ static irqreturn_t acp_irq_thread(int irq, void *context) static irqreturn_t acp_irq_handler(int irq, void *dev_id) { + struct amd_sdw_manager *amd_manager; struct snd_sof_dev *sdev = dev_id; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + struct acp_dev_data *adata = sdev->pdata->hw_pdata; unsigned int base = desc->dsp_intr_base; unsigned int val; + int irq_flag = 0; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); if (val & ACP_DSP_TO_HOST_IRQ) { @@ -387,7 +390,38 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) return IRQ_WAKE_THREAD; } - return IRQ_NONE; + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); + if (val & ACP_SDW0_IRQ_MASK) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SDW0_IRQ_MASK); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + + if (val & ACP_ERROR_IRQ_MASK) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW0_I2S_ERROR_REASON, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW1_I2S_ERROR_REASON, 0); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_ERROR_STATUS, 0); + irq_flag = 1; + } + + if (desc->ext_intr_stat1) { + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1); + if (val & ACP_SDW1_IRQ_MASK) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, + ACP_SDW1_IRQ_MASK); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + } + if (irq_flag) + return IRQ_HANDLED; + else + return IRQ_NONE; } static int acp_power_on(struct snd_sof_dev *sdev) @@ -443,6 +477,8 @@ static int acp_reset(struct snd_sof_dev *sdev) if (desc->ext_intr_enb) snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01); + if (desc->ext_intr_cntl) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK); return ret; } diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index e88d01330ec7..2058dae32659 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -93,6 +93,9 @@ #define PROBE_STATUS_BIT BIT(31) #define ACP_FIRMWARE_SIGNATURE 0x100 +#define ACP_ERROR_IRQ_MASK BIT(29) +#define ACP_SDW0_IRQ_MASK BIT(21) +#define ACP_SDW1_IRQ_MASK BIT(2) #define ACP_DEFAULT_SRAM_LENGTH 0x00080000 #define ACP_SRAM_PAGE_COUNT 128 @@ -184,7 +187,9 @@ struct sof_amd_acp_desc { unsigned int host_bridge_id; u32 pgfsm_base; u32 ext_intr_enb; + u32 ext_intr_cntl; u32 ext_intr_stat; + u32 ext_intr_stat1; u32 dsp_intr_base; u32 sram_pte_offset; u32 hw_semaphore_offset; -- cgit v1.2.3 From 14d89e55dec9c4e49d196b9d5d659d02dcc8252b Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:43 +0530 Subject: ASoC: SOF: amd: Add Soundwire DAI configuration support for AMD platforms Add support for configuring AMD Soundwire DAI from topology. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-10-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai-amd.h | 7 +++++++ include/sound/sof/dai.h | 2 ++ include/uapi/sound/sof/tokens.h | 4 ++++ sound/soc/sof/ipc3-pcm.c | 25 +++++++++++++++++++++++++ sound/soc/sof/ipc3-topology.c | 40 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 1 + sound/soc/sof/topology.c | 5 +++++ 7 files changed, 84 insertions(+) (limited to 'sound/soc') diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 9df7ac824efe..59cd014392c1 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -26,4 +26,11 @@ struct sof_ipc_dai_acpdmic_params { uint32_t pdm_ch; } __packed; +/* ACP_SDW Configuration Request - SOF_IPC_DAI_AMD_SDW_CONFIG */ +struct sof_ipc_dai_acp_sdw_params { + struct sof_ipc_hdr hdr; + u32 rate; + u32 channels; +} __packed; + #endif diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 4773a5f616a4..0764a80c17a9 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -89,6 +89,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_SP_VIRTUAL, /**< AMD ACP SP VIRTUAL */ SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ + SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ }; /* general purpose DAI configuration */ @@ -119,6 +120,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_acp_params acphs; struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; + struct sof_ipc_dai_acp_sdw_params acp_sdw; }; } __packed; diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index ee5708934614..6bf00c09d30d 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -218,4 +218,8 @@ #define SOF_TKN_IMX_MICFIL_RATE 2000 #define SOF_TKN_IMX_MICFIL_CH 2001 +/* ACP SDW */ +#define SOF_TKN_AMD_ACP_SDW_RATE 2100 +#define SOF_TKN_AMD_ACP_SDW_CH 2101 + #endif diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 330f04bcd75d..35769dd7905e 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -395,6 +395,31 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_SDW: + /* change the default trigger sequence as per HW implementation */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + SND_SOC_DPCM_TRIGGER_POST; + } + + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = + SND_SOC_DPCM_TRIGGER_POST; + } + rate->min = private->dai_config->acp_sdw.rate; + rate->max = private->dai_config->acp_sdw.rate; + channels->min = private->dai_config->acp_sdw.channels; + channels->max = private->dai_config->acp_sdw.channels; + + dev_dbg(component->dev, + "AMD_SDW rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index a8832a1c1a24..0970dbdfa78a 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -298,6 +298,14 @@ static const struct sof_topology_token micfil_pdm_tokens[] = { offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)}, }; +/* ACP_SDW */ +static const struct sof_topology_token acp_sdw_tokens[] = { + {SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_sdw_params, rate)}, + {SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_sdw_params, channels)}, +}; + /* Core tokens */ static const struct sof_topology_token core_tokens[] = { {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, @@ -336,6 +344,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { [SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)}, [SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens", micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)}, + [SOF_ACP_SDW_TOKENS] = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)}, }; /** @@ -1315,6 +1324,34 @@ static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_ return 0; } +static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* parse the required set of ACP_SDW tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* init IPC */ + config->hdr.size = size; + dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n", + config->acp_sdw.rate, config->acp_sdw.channels); + + /* set config for all DAI's with name matching the link name */ + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1629,6 +1666,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_MEDIATEK_AFE: ret = sof_link_afe_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_SDW: + ret = sof_link_acp_sdw_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 8874ee5f557f..f98242a404db 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -276,6 +276,7 @@ enum sof_tokens { SOF_ACPDMIC_TOKENS, SOF_ACPI2S_TOKENS, SOF_MICFIL_TOKENS, + SOF_ACP_SDW_TOKENS, /* this should be the last */ SOF_TOKEN_COUNT, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 617a225fff24..25fb0d1443b6 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -297,6 +297,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL}, {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, + {"ACP_SDW", SOF_DAI_AMD_SDW}, }; @@ -1968,6 +1969,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ token_id = SOF_MICFIL_TOKENS; num_tuples += token_list[SOF_MICFIL_TOKENS].count; break; + case SOF_DAI_AMD_SDW: + token_id = SOF_ACP_SDW_TOKENS; + num_tuples += token_list[SOF_ACP_SDW_TOKENS].count; + break; default: break; } -- cgit v1.2.3 From 5f97c59a77421a5e8aa3b5c4cbdda66a3c956e44 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:44 +0530 Subject: ASoC: SOF: amd: add machine select logic for soundwire based platforms Add machine select logic for soundwire endpoints for AMD platforms. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-11-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-common.c | 65 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index 2d72c6d55dc8..0fc4e20ec673 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -118,16 +118,72 @@ void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags) &panic_info, stack, AMD_STACK_DUMP_SIZE); } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE) +static int amd_sof_sdw_get_slave_info(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data = sdev->pdata->hw_pdata; + + return sdw_amd_get_slave_info(acp_data->sdw); +} + +static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev) +{ + struct snd_soc_acpi_mach *mach; + const struct snd_soc_acpi_link_adr *link; + struct acp_dev_data *acp_data = sdev->pdata->hw_pdata; + int ret, i; + + if (acp_data->info.count) { + ret = amd_sof_sdw_get_slave_info(sdev); + if (ret) { + dev_info(sdev->dev, "failed to read slave information\n"); + return NULL; + } + for (mach = sdev->pdata->desc->alt_machines; mach; mach++) { + if (!mach->links) + break; + link = mach->links; + for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { + if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, + acp_data->sdw->ids, + acp_data->sdw->num_slaves)) + break; + } + if (i == acp_data->info.count || !link->num_adr) + break; + } + if (mach && mach->link_mask) { + mach->mach_params.links = mach->links; + mach->mach_params.link_mask = mach->link_mask; + mach->mach_params.platform = dev_name(sdev->dev); + return mach; + } + } + dev_info(sdev->dev, "No SoundWire machine driver found\n"); + return NULL; +} + +#else +static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev) +{ + return NULL; +} +#endif + struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach *mach = NULL; - mach = snd_soc_acpi_find_machine(desc->machines); + if (desc->machines) + mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { - dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); - return NULL; + mach = amd_sof_sdw_machine_select(sdev); + if (!mach) { + dev_warn(sdev->dev, "No matching ASoC machine driver found\n"); + return NULL; + } } sof_pdata->tplg_filename = mach->sof_tplg_filename; @@ -204,5 +260,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON); MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); MODULE_DESCRIPTION("ACP SOF COMMON Driver"); MODULE_LICENSE("Dual BSD/GPL"); -- cgit v1.2.3 From 8af5c7e9cc89ebc6427ef8fde1de77e88ddd3e05 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:45 +0530 Subject: ASoC: SOF: amd: update descriptor fields for acp6.3 based platform Update acp descriptor fields for acp6.3 version based platform. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-12-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 3 +++ sound/soc/sof/amd/acp.h | 2 ++ sound/soc/sof/amd/pci-acp63.c | 7 +++++++ 3 files changed, 12 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 7ba6492b8e99..c1bdc028a61a 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -65,7 +65,10 @@ /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 #define ACP5X_EXT_INTR_STAT 0x1808 +#define ACP6X_EXTERNAL_INTR_ENB 0x1A00 +#define ACP6X_EXTERNAL_INTR_CNTL 0x1A04 #define ACP6X_EXT_INTR_STAT 0x1A0C +#define ACP6X_EXT_INTR_STAT1 0x1A10 #define ACP3X_DSP_SW_INTR_BASE 0x1814 #define ACP5X_DSP_SW_INTR_BASE 0x1814 diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 2058dae32659..e94713d7ff1d 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -96,8 +96,10 @@ #define ACP_ERROR_IRQ_MASK BIT(29) #define ACP_SDW0_IRQ_MASK BIT(21) #define ACP_SDW1_IRQ_MASK BIT(2) +#define SDW_ACPI_ADDR_ACP63 5 #define ACP_DEFAULT_SRAM_LENGTH 0x00080000 #define ACP_SRAM_PAGE_COUNT 128 +#define ACP6X_SDW_MAX_MANAGER_COUNT 2 enum clock_source { ACP_CLOCK_96M = 0, diff --git a/sound/soc/sof/amd/pci-acp63.c b/sound/soc/sof/amd/pci-acp63.c index bceb94ac80a9..eeaa12cceb23 100644 --- a/sound/soc/sof/amd/pci-acp63.c +++ b/sound/soc/sof/amd/pci-acp63.c @@ -31,12 +31,19 @@ static const struct sof_amd_acp_desc acp63_chip_info = { .rev = 6, .host_bridge_id = HOST_BRIDGE_ACP63, .pgfsm_base = ACP6X_PGFSM_BASE, + .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, + .ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL, .ext_intr_stat = ACP6X_EXT_INTR_STAT, + .ext_intr_stat1 = ACP6X_EXT_INTR_STAT1, .dsp_intr_base = ACP6X_DSP_SW_INTR_BASE, .sram_pte_offset = ACP6X_SRAM_PTE_OFFSET, .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0, .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL, .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0, + .sdw_max_link_count = ACP6X_SDW_MAX_MANAGER_COUNT, + .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP63, + .reg_start_addr = ACP6x_REG_START, + .reg_end_addr = ACP6x_REG_END, }; static const struct sof_dev_desc acp63_desc = { -- cgit v1.2.3 From 2188c2cfaa4f431c1d537bb029a6e9b0810b7e7f Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:46 +0530 Subject: ASoC: SOF: amd: select soundwire dependency flag for acp6.3 based platform Select SoundWire dependency flag for acp6.3 based platform for SoundWire configuration. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-13-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 1cea1d75130c..c3bbe6c70fb2 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -81,6 +81,7 @@ config SND_SOC_SOF_AMD_ACP63 tristate "SOF support for ACP6.3 platform" depends on SND_SOC_SOF_PCI select SND_SOC_SOF_AMD_COMMON + select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE help Select this option for SOF support on AMD ACP6.3 version based platforms. -- cgit v1.2.3 From 260b08aed4a770335ece16781d8023e9ff488ae0 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 29 Jan 2024 11:21:47 +0530 Subject: ASoC: SOF: amd: refactor acp driver pm ops Refactor acp driver pm ops to support SoundWire interface. When SoundWire configuration is enabled, In case of ClockStopMode, DSP soft reset should be applied and for rest of the scenarios acp init/deinit sequence should be invoked. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240129055147.1493853-14-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-dsp-offset.h | 3 ++ sound/soc/sof/amd/acp.c | 65 +++++++++++++++++++++++++++++++++++--- sound/soc/sof/amd/acp.h | 4 +++ 3 files changed, 67 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index c1bdc028a61a..59afbe2e0f42 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -103,4 +103,7 @@ /* Cache window registers */ #define ACP_DSP0_CACHE_OFFSET0 0x0420 #define ACP_DSP0_CACHE_SIZE0 0x0424 + +#define ACP_SW0_EN 0x3000 +#define ACP_SW1_EN 0x3C00 #endif diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 7a34faae9889..920fead2d93d 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -482,6 +482,31 @@ static int acp_reset(struct snd_sof_dev *sdev) return ret; } +static int acp_dsp_reset(struct snd_sof_dev *sdev) +{ + unsigned int val; + int ret; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_ASSERT_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, + val & ACP_DSP_SOFT_RESET_DONE_MASK, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) { + dev_err(sdev->dev, "timeout asserting reset\n"); + return ret; + } + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_RELEASE_RESET); + + ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val, + ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US); + if (ret < 0) + dev_err(sdev->dev, "timeout in releasing reset\n"); + + return ret; +} + static int acp_init(struct snd_sof_dev *sdev) { int ret; @@ -498,10 +523,34 @@ static int acp_init(struct snd_sof_dev *sdev) return acp_reset(sdev); } +static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev) +{ + struct acp_dev_data *acp_data; + u32 sdw0_en, sdw1_en; + + acp_data = sdev->pdata->hw_pdata; + if (!acp_data->sdw) + return false; + + sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN); + sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN); + acp_data->sdw_en_stat = sdw0_en || sdw1_en; + return acp_data->sdw_en_stat; +} + int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state) { int ret; + /* When acp_reset() function is invoked, it will apply ACP SOFT reset and + * DSP reset. ACP Soft reset sequence will cause all ACP IP registers will + * be reset to default values which will break the ClockStop Mode functionality. + * Add a condition check to apply DSP reset when SoundWire ClockStop mode + * is selected. For the rest of the scenarios, apply acp reset sequence. + */ + if (check_acp_sdw_enable_status(sdev)) + return acp_dsp_reset(sdev); + ret = acp_reset(sdev); if (ret) { dev_err(sdev->dev, "ACP Reset failed\n"); @@ -517,13 +566,19 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_resume(struct snd_sof_dev *sdev) { int ret; + struct acp_dev_data *acp_data; - ret = acp_init(sdev); - if (ret) { - dev_err(sdev->dev, "ACP Init failed\n"); - return ret; + acp_data = sdev->pdata->hw_pdata; + if (!acp_data->sdw_en_stat) { + ret = acp_init(sdev); + if (ret) { + dev_err(sdev->dev, "ACP Init failed\n"); + return ret; + } + return acp_memory_init(sdev); + } else { + return acp_dsp_reset(sdev); } - return acp_memory_init(sdev); } EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index e94713d7ff1d..947068da39b5 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -31,6 +31,9 @@ #define ACP_ASSERT_RESET 0x01 #define ACP_RELEASE_RESET 0x00 #define ACP_SOFT_RESET_DONE_MASK 0x00010001 +#define ACP_DSP_ASSERT_RESET 0x04 +#define ACP_DSP_RELEASE_RESET 0x00 +#define ACP_DSP_SOFT_RESET_DONE_MASK 0x00050004 #define ACP_DSP_INTR_EN_MASK 0x00000001 #define ACP3X_SRAM_PTE_OFFSET 0x02050000 @@ -242,6 +245,7 @@ struct acp_dev_data { bool enable_fw_debug; bool is_dram_in_use; bool is_sram_in_use; + bool sdw_en_stat; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); -- cgit v1.2.3 From cd2a2388614fcf2b9b626332c0da53a2c6cbf2ee Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 2 Feb 2024 14:06:17 +0000 Subject: ASoC: cs42l43: Add clear of stashed pointer on component remove If the component is removed the stashed component pointer in the CODECs private struct should also be cleared to prevent use of a stale pointer. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20240202140619.1068560-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 2c402086924d..9e1deb3242cb 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2111,10 +2111,18 @@ static int cs42l43_component_probe(struct snd_soc_component *component) return 0; } +static void cs42l43_component_remove(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + priv->component = NULL; +} + static const struct snd_soc_component_driver cs42l43_component_drv = { .name = "cs42l43-codec", .probe = cs42l43_component_probe, + .remove = cs42l43_component_remove, .set_sysclk = cs42l43_set_sysclk, .set_jack = cs42l43_set_jack, -- cgit v1.2.3 From 7fa1a01ba6cb64bc24e7ba0dbee589f3f09f3cf7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 2 Feb 2024 14:06:18 +0000 Subject: ASoC: cs42l43: Sync the hp ilimit works when removing the component Synchronise the headphone ilimit work functions when removing the component. These can only trigger whilst the headphone is enabled which shouldn't be possible once the component is removed but the works rely on the stashed component pointer so they should be shut down before the code moves on from component remove. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20240202140619.1068560-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 9e1deb3242cb..c84d5952cdb5 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2115,6 +2115,9 @@ static void cs42l43_component_remove(struct snd_soc_component *component) { struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + cancel_work_sync(&priv->hp_ilimit_work); + cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); + priv->component = NULL; } -- cgit v1.2.3 From 3ef9f445ddb1e061ce497f54ca75bbaec52a3a46 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 2 Feb 2024 14:06:19 +0000 Subject: ASoC: cs42l43: Shut down jack detection on component remove Disable the jack detection and sync in any currently running work when the component is removed. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20240202140619.1068560-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index c84d5952cdb5..256767ef4c03 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2115,6 +2115,13 @@ static void cs42l43_component_remove(struct snd_soc_component *component) { struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + cs42l43_set_jack(priv->component, NULL, NULL); + + cancel_delayed_work_sync(&priv->bias_sense_timeout); + cancel_delayed_work_sync(&priv->tip_sense_work); + cancel_delayed_work_sync(&priv->button_press_work); + cancel_work_sync(&priv->button_release_work); + cancel_work_sync(&priv->hp_ilimit_work); cancel_delayed_work_sync(&priv->hp_ilimit_clear_work); -- cgit v1.2.3 From 69f8336e2913f12795fa0ec986bf63a8461ebfb9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Feb 2024 22:22:01 +0100 Subject: ASoC: SOF: amd: fix SND_AMD_SOUNDWIRE_ACPI dependencies The snd-amd-sdw-acpi.ko module is under CONFIG_SND_SOC_AMD_ACP_COMMON but selected from SoF, which causes build failures in some randconfig builds that enable SOF but not ACP: WARNING: unmet direct dependencies detected for SND_AMD_SOUNDWIRE_ACPI Depends on [n]: SOUND [=m] && SND [=m] && SND_SOC [=m] && SND_SOC_AMD_ACP_COMMON [=n] && ACPI [=y] Selected by [m]: - SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE [=m] && SOUND [=m] && SND [=m] && SND_SOC [=m] && SND_SOC_SOF_TOPLEVEL [=y] && SND_SOC_SOF_AMD_TOPLEVEL [=m] && ACPI [=y] ERROR: modpost: "amd_sdw_scan_controller" [sound/soc/sof/amd/snd-sof-amd-acp.ko] undefined! Change the Makefile and Kconfig to allow it to get built regardless of CONFIG_SND_SOC_AMD_ACP_COMMON. Fixes: d948218424bf ("ASoC: SOF: amd: add code for invoking soundwire manager helper functions") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240204212207.3158914-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/amd/Makefile | 2 +- sound/soc/amd/acp/Kconfig | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 82e1cf864a40..ebbe49c2bbff 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ -obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ +obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/ obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/ obj-$(CONFIG_SND_SOC_AMD_PS) += ps/ diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index b3105ba9c3a3..30590a23ad63 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -101,13 +101,6 @@ config SND_SOC_AMD_MACH_COMMON help This option enables common Machine driver module for ACP. -config SND_AMD_SOUNDWIRE_ACPI - tristate "AMD SoundWire ACPI Support" - depends on ACPI - help - This options enables ACPI helper functions for SoundWire - interface for AMD platforms. - config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" depends on X86 && PCI && I2C @@ -123,3 +116,10 @@ config SND_SOC_AMD_SOF_MACH This option enables SOF sound card support for ACP audio. endif # SND_SOC_AMD_ACP_COMMON + +config SND_AMD_SOUNDWIRE_ACPI + tristate + depends on ACPI + help + This options enables ACPI helper functions for SoundWire + interface for AMD platforms. -- cgit v1.2.3 From b4956275bf88a5708200713707c6c293648d39a9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Feb 2024 22:22:02 +0100 Subject: ASoC: fix SND_SOC_WCD939X dependencies SND_SOC_WCD939X has an optional dependency on TYPEC, so the newly added SND_SOC_WCD939X_SDW option that selects it needs the same dependency, otherwise it can fail randconfig builds like: WARNING: unmet direct dependencies detected for SND_SOC_WCD939X Depends on [m]: SOUND [=y] && SND [=y] && SND_SOC [=y] && SND_SOC_WCD939X_SDW [=y] && (SOUNDWIRE [=y] || !SOUNDWIRE [=y]) && (TYPEC [=m] || !TYPEC [=m]) Selected by [y]: - SND_SOC_WCD939X_SDW [=y] && SOUND [=y] && SND [=y] && SND_SOC [=y] && SOUNDWIRE [=y] arm-linux-gnueabi-ld: sound/soc/codecs/wcd939x.o: in function `wcd939x_soc_codec_remove': wcd939x.c:(.text+0x1950): undefined reference to `wcd_clsh_ctrl_free' arm-linux-gnueabi-ld: sound/soc/codecs/wcd939x.o: in function `wcd939x_codec_ear_dac_event': wcd939x.c:(.text+0x35d8): undefined reference to `wcd_clsh_ctrl_set_state' arm-linux-gnueabi-ld: sound/soc/codecs/wcd939x.o: in function `wcd939x_codec_enable_hphr_pa': wcd939x.c:(.text+0x39b0): undefined reference to `wcd_clsh_ctrl_set_state' arm-linux-gnueabi-ld: wcd939x.c:(.text+0x39dc): undefined reference to `wcd_clsh_set_hph_mode' arm-linux-gnueabi-ld: wcd939x.c:(.text+0x3bc0): undefined reference to `wcd_clsh_ctrl_set_state' Fixes: be2af391cea0 ("ASoC: codecs: Add WCD939x Soundwire devices driver") Signed-off-by: Arnd Bergmann Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20240204212207.3158914-2-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 75d88bd1dc6f..58ee431edfd8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2084,6 +2084,7 @@ config SND_SOC_WCD939X config SND_SOC_WCD939X_SDW tristate "WCD9390/WCD9395 Codec - SDW" + depends on TYPEC || !TYPEC select SND_SOC_WCD939X select SND_SOC_WCD_MBHC select REGMAP_IRQ -- cgit v1.2.3 From 8f501d29c7a6a03303c1a085fd27948e1edb0c90 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 4 Feb 2024 18:14:24 +0900 Subject: ASoC: pxa: remove duplicated CONFIG_SND_PXA2XX_AC97 entry 'config SND_PXA2XX_AC97' is already present in sound/arm/Kconfig with a prompt. Commit 734c2d4bb7cf ("[ALSA] ASoC pxa2xx build support") redundantly added the second one to sound/soc/pxa/Kconfig. Remove it. Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20240204091424.38306-1-masahiroy@kernel.org Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f03c74809324..e05d6ce4c8fa 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -8,9 +8,6 @@ config SND_PXA2XX_SOC the PXA2xx AC97, I2S or SSP interface. You will also need to select the audio interfaces to support below. -config SND_PXA2XX_AC97 - tristate - config SND_PXA2XX_SOC_AC97 tristate "SoC AC97 support for PXA2xx" depends on SND_PXA2XX_SOC -- cgit v1.2.3 From 1b4217ebbb3e9d9b014db660618a4ddb61b3c321 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 8 Feb 2024 11:23:59 +0100 Subject: ASoC: Intel: avs: Add topology parsing support for initial config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add topology parsing for initial config. Reviewed-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20240208102400.2497791-3-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/topology.c | 164 ++++++++++++++++++++++++++++++++++++++++- sound/soc/intel/avs/topology.h | 13 ++++ 2 files changed, 175 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 778236d3fd28..e5376a939dbd 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1118,6 +1118,21 @@ static const struct avs_tplg_token_parser module_parsers[] = { .offset = offsetof(struct avs_tplg_module, ctl_id), .parse = avs_parse_byte_token, }, + { + .token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, num_config_ids), + .parse = avs_parse_byte_token, + }, +}; + +static const struct avs_tplg_token_parser init_config_parsers[] = { + { + .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = 0, + .parse = avs_parse_word_token, + }, }; static struct avs_tplg_module * @@ -1125,17 +1140,50 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline struct snd_soc_tplg_vendor_array *tuples, u32 block_size) { struct avs_tplg_module *module; + u32 esize; int ret; + /* See where config id block starts. */ + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_MOD_INIT_CONFIG_ID_U32, &esize); + if (ret) + return ERR_PTR(ret); + module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL); if (!module) return ERR_PTR(-ENOMEM); ret = avs_parse_tokens(comp, module, module_parsers, - ARRAY_SIZE(module_parsers), tuples, block_size); + ARRAY_SIZE(module_parsers), tuples, esize); if (ret < 0) return ERR_PTR(ret); + block_size -= esize; + /* Parse trailing config ids if any. */ + if (block_size) { + u32 num_config_ids = module->num_config_ids; + u32 *config_ids; + + if (!num_config_ids) + return ERR_PTR(-EINVAL); + + config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids), + GFP_KERNEL); + if (!config_ids) + return ERR_PTR(-ENOMEM); + + tuples = avs_tplg_vendor_array_at(tuples, esize); + ret = parse_dictionary_entries(comp, tuples, block_size, + config_ids, num_config_ids, sizeof(*config_ids), + AVS_TKN_MOD_INIT_CONFIG_ID_U32, + init_config_parsers, + ARRAY_SIZE(init_config_parsers)); + if (ret) + return ERR_PTR(ret); + + module->config_ids = config_ids; + } + module->owner = owner; INIT_LIST_HEAD(&module->node); @@ -1416,6 +1464,82 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o return template; } +static const struct avs_tplg_token_parser mod_init_config_parsers[] = { + { + .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_init_config, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_INIT_CONFIG_PARAM_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_init_config, param), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_INIT_CONFIG_LENGTH_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_init_config, length), + .parse = avs_parse_word_token, + }, +}; + +static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + int ret, i; + + /* Parse tuple section telling how many init configs there are. */ + ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs, + &tplg->num_init_configs, + sizeof(*tplg->init_configs), + AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32); + if (ret) + return ret; + + block_size -= le32_to_cpu(tuples->size); + /* With header parsed, move on to parsing entries. */ + tuples = avs_tplg_vendor_array_next(tuples); + + for (i = 0; i < tplg->num_init_configs && block_size > 0; i++) { + struct avs_tplg_init_config *config = &tplg->init_configs[i]; + struct snd_soc_tplg_vendor_array *tmp; + void *init_config_data; + u32 esize; + + /* + * Usually to get section length we search for first token of next group of data, + * but in this case we can't as tuples are followed by raw data. + */ + tmp = avs_tplg_vendor_array_next(tuples); + esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); + + ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), + AVS_TKN_MOD_INIT_CONFIG_ID_U32, + mod_init_config_parsers, + ARRAY_SIZE(mod_init_config_parsers)); + + block_size -= esize; + + /* handle raw data section */ + init_config_data = (void *)tuples + esize; + esize = config->length; + + config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL); + if (!config->data) + return -ENOMEM; + + tuples = init_config_data + esize; + block_size -= esize; + } + + return 0; +} + static int avs_route_load(struct snd_soc_component *comp, int index, struct snd_soc_dapm_route *route) { @@ -1571,6 +1695,7 @@ static int avs_manifest(struct snd_soc_component *comp, int index, struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array; struct avs_soc_component *acomp = to_avs_soc_component(comp); size_t remaining = le32_to_cpu(manifest->priv.size); + bool has_init_config = true; u32 offset; int ret; @@ -1668,8 +1793,43 @@ static int avs_manifest(struct snd_soc_component *comp, int index, remaining -= offset; tuples = avs_tplg_vendor_array_at(tuples, offset); + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset); + if (ret) { + dev_err(comp->dev, "condpath lookup failed: %d\n", ret); + return ret; + } + /* Bindings dictionary. */ - return avs_tplg_parse_bindings(comp, tuples, remaining); + ret = avs_tplg_parse_bindings(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32, &offset); + if (ret == -ENOENT) { + dev_dbg(comp->dev, "init config lookup failed: %d\n", ret); + has_init_config = false; + } else if (ret) { + dev_err(comp->dev, "init config lookup failed: %d\n", ret); + return ret; + } + + if (!has_init_config) + return 0; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + /* Initial configs dictionary. */ + ret = avs_tplg_parse_initial_configs(comp, tuples, remaining); + if (ret < 0) + return ret; + + return 0; } #define AVS_CONTROL_OPS_VOLUME 257 diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 6e1c8e9b2496..6a59dd766603 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -33,6 +33,9 @@ struct avs_tplg { u32 num_pplcfgs; struct avs_tplg_binding *bindings; u32 num_bindings; + u32 num_condpath_tmpls; + struct avs_tplg_init_config *init_configs; + u32 num_init_configs; struct list_head path_tmpl_list; }; @@ -147,6 +150,14 @@ struct avs_tplg_path_template { struct list_head node; }; +struct avs_tplg_init_config { + u32 id; + + u8 param; + size_t length; + void *data; +}; + struct avs_tplg_path { u32 id; @@ -183,6 +194,8 @@ struct avs_tplg_module { u8 domain; struct avs_tplg_modcfg_ext *cfg_ext; u32 ctl_id; + u32 num_config_ids; + u32 *config_ids; struct avs_tplg_pipeline *owner; /* Pipeline modules management. */ -- cgit v1.2.3 From 8a49ef789b1be68242624d460df2ada8087308a7 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 8 Feb 2024 11:24:00 +0100 Subject: ASoC: Intel: avs: Send initial config to module if present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are initial configs to send to module on init do send them. Reviewed-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20240208102400.2497791-4-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/path.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index 3aa16ee8d34c..e785fc2a7008 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -547,6 +547,33 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod return avs_modext_create(adev, mod); } +static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_soc_component *acomp; + + acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp); + + u32 num_ids = mod->template->num_config_ids; + u32 *ids = mod->template->config_ids; + + for (int i = 0; i < num_ids; i++) { + struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]]; + size_t len = config->length; + void *data = config->data; + u32 param = config->param; + int ret; + + ret = avs_ipc_set_large_config(adev, mod->module_id, mod->instance_id, + param, data, len); + if (ret) { + dev_err(adev->dev, "send initial module config failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + } + + return 0; +} + static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod) { kfree(mod); @@ -580,6 +607,12 @@ avs_path_module_create(struct avs_dev *adev, return ERR_PTR(ret); } + ret = avs_path_module_send_init_configs(adev, mod); + if (ret) { + kfree(mod); + return ERR_PTR(ret); + } + return mod; } -- cgit v1.2.3 From fd38b4e41096f5c72a1623ba5984e2d4d2a799c4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Feb 2024 11:50:11 +0100 Subject: ASoC: codecs: constify static sdw_slave_ops struct The struct sdw_slave_ops is not modified and sdw_driver takes pointer to const, so make it a const for code safety. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240208105011.128294-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98363.c | 2 +- sound/soc/codecs/max98373-sdw.c | 2 +- sound/soc/codecs/rt1017-sdca-sdw.c | 2 +- sound/soc/codecs/rt712-sdca-dmic.c | 2 +- sound/soc/codecs/rt712-sdca-sdw.c | 2 +- sound/soc/codecs/rt722-sdca-sdw.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index a2cca3436c68..950105e5bffd 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -314,7 +314,7 @@ static int max98363_update_status(struct sdw_slave *slave, return max98363_io_init(slave); } -static struct sdw_slave_ops max98363_slave_ops = { +static const struct sdw_slave_ops max98363_slave_ops = { .read_prop = max98363_read_prop, .update_status = max98363_update_status, }; diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index b5cb951af570..383e551f3bc7 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -821,7 +821,7 @@ static int max98373_bus_config(struct sdw_slave *slave, * slave_ops: callbacks for get_clock_stop_mode, clock_stop and * port_prep are not defined for now */ -static struct sdw_slave_ops max98373_slave_ops = { +static const struct sdw_slave_ops max98373_slave_ops = { .read_prop = max98373_read_prop, .update_status = max98373_update_status, .bus_config = max98373_bus_config, diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 7295f44c77eb..4dbbd8bdaaac 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -520,7 +520,7 @@ static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { { "DP2TX", NULL, "V Sense" }, }; -static struct sdw_slave_ops rt1017_sdca_slave_ops = { +static const struct sdw_slave_ops rt1017_sdca_slave_ops = { .read_prop = rt1017_sdca_read_prop, .update_status = rt1017_sdca_update_status, }; diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index ba08d03e717c..0926b26619bd 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -944,7 +944,7 @@ static const struct dev_pm_ops rt712_sdca_dmic_pm = { }; -static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { .read_prop = rt712_sdca_dmic_read_prop, .update_status = rt712_sdca_dmic_update_status, }; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6b644a89c589..01ac555cd79b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -331,7 +331,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt712_sdca_slave_ops = { +static const struct sdw_slave_ops rt712_sdca_slave_ops = { .read_prop = rt712_sdca_read_prop, .interrupt_callback = rt712_sdca_interrupt_callback, .update_status = rt712_sdca_update_status, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e24b9cbdc10c..eb76f4c675b6 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -362,7 +362,7 @@ io_error: return ret; } -static struct sdw_slave_ops rt722_sdca_slave_ops = { +static const struct sdw_slave_ops rt722_sdca_slave_ops = { .read_prop = rt722_sdca_read_prop, .interrupt_callback = rt722_sdca_interrupt_callback, .update_status = rt722_sdca_update_status, -- cgit v1.2.3 From 9be229ffc7a46c645a39d5993a2709d9936e046a Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:22 -0600 Subject: ASoC: Intel: sof_rt5682: board id cleanup for jsl boards Many board configs are duplicated since codec and amplifier type are removed from board quirk. Introduce "jsl_rt5682_def" board to reduce the number of jsl board configs. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 25 +---------------------- sound/soc/intel/common/soc-acpi-intel-jsl-match.c | 10 ++++----- 2 files changed, 6 insertions(+), 29 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index cd50f26d1edb..3763985f570f 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -810,30 +810,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1)), }, { - .name = "jsl_rt5682_rt1015", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "jsl_rt5682_mx98360", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "jsl_rt5682_rt1015p", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1)), - }, - { - .name = "jsl_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0)), - }, - { - .name = "jsl_rt5650", + .name = "jsl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1)), diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 342bbbb48ca7..a6ac2525df17 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -66,28 +66,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_rt1015", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_rt1015p", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt1015p_spk, .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682_mx98360", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg", }, { .comp_ids = &rt5682_rt5682s_hp, - .drv_name = "jsl_rt5682", + .drv_name = "jsl_rt5682_def", .sof_tplg_filename = "sof-jsl-rt5682.tplg", }, { @@ -107,7 +107,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { }, { .id = "10EC5650", - .drv_name = "jsl_rt5650", + .drv_name = "jsl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rt5650_spk, .sof_tplg_filename = "sof-jsl-rt5650.tplg", -- cgit v1.2.3 From dbda8647fb9ff39a957018673249d6bc0b1ccbf1 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:23 -0600 Subject: ASoC: Intel: sof_rt5682: board id cleanup for tgl boards Many board configs are duplicated since codec and amplifier type are removed from board quirk. Introduce "tgl_rt5682_def" board to reduce the number of tgl board configs. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 20 +------------------- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 6 +++--- 2 files changed, 4 insertions(+), 22 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 3763985f570f..9644ae22c6ee 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -816,25 +816,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1)), }, { - .name = "tgl_mx98357_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "tgl_rt1011_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "tgl_mx98373_rt5682", + .name = "tgl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index e5f721ba5ed4..0fba0a60d9c7 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -495,21 +495,21 @@ static const struct snd_soc_acpi_codecs tgl_lt6911_hdmi = { struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_mx98357_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_codecs, .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_mx98373_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_max98373_amp, .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg", }, { .comp_ids = &tgl_rt5682_rt5682s_hp, - .drv_name = "tgl_rt1011_rt5682", + .drv_name = "tgl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_rt1011_amp, .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg", -- cgit v1.2.3 From 41333c351da82a2277bb232aa74cda4181041328 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:24 -0600 Subject: ASoC: Intel: sof_rt5682: board id cleanup for adl boards Many board configs are duplicated since codec and amplifier type are removed from board quirk. Introduce "adl_rt5682_def" board to reduce the number of adl board configs. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 46 +---------------------- sound/soc/intel/common/soc-acpi-intel-adl-match.c | 12 +++--- 2 files changed, 7 insertions(+), 51 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 9644ae22c6ee..e556bbd660c5 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -825,7 +825,7 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "adl_mx98373_rt5682", + .name = "adl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | @@ -840,41 +840,6 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, - { - .name = "adl_max98390_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_mx98360_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "adl_rt1019_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, { .name = "adl_rt5682_c1_h02", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | @@ -883,15 +848,6 @@ static const struct platform_device_id board_ids[] = { /* SSP 0 and SSP 2 are used for HDMI IN */ SOF_HDMI_CAPTURE_SSP_MASK(0x5)), }, - { - .name = "adl_rt5650", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, { .name = "rpl_mx98357_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index d3d913458c60..0da79a3ba1f0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -500,7 +500,7 @@ static struct snd_soc_acpi_codecs adl_rt5650_amp = { struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_mx98373_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg", @@ -514,7 +514,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_mx98360_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98360a_amp, .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", @@ -542,7 +542,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_rt1019_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt1019p_amp, .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg", @@ -568,7 +568,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_max98390_rt5682", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98390_amp, .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", @@ -582,7 +582,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, - .drv_name = "adl_rt5682", + .drv_name = "adl_rt5682_def", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, { @@ -609,7 +609,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .id = "10EC5650", - .drv_name = "adl_rt5650", + .drv_name = "adl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_rt5650_amp, .sof_tplg_filename = "sof-adl-rt5650.tplg", -- cgit v1.2.3 From 19ec6b2ef8b6d1320866d2a2508cd16f95738640 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:25 -0600 Subject: ASoC: Intel: sof_rt5682: board id cleanup for rpl boards Many board configs are duplicated since codec and amplifier type are removed from board quirk. Introduce "rpl_rt5682_def" board to reduce the number of rpl board configs. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 11 +---------- sound/soc/intel/common/soc-acpi-intel-rpl-match.c | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index e556bbd660c5..995372091387 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -856,16 +856,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(4)), }, { - .name = "rpl_mx98360_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - }, - { - .name = "rpl_rt1019_rt5682", + .name = "rpl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_RT5682_SSP_AMP(1) | diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index c0a643f4725a..00a21af210fa 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -395,7 +395,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { }, { .comp_ids = &rpl_rt5682_hp, - .drv_name = "rpl_mx98360_rt5682", + .drv_name = "rpl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98360a_amp, .sof_tplg_filename = "sof-rpl-max98360a-rt5682.tplg", @@ -423,7 +423,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { }, { .comp_ids = &rpl_rt5682_hp, - .drv_name = "rpl_rt1019_rt5682", + .drv_name = "rpl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_rt1019p_amp, .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg", -- cgit v1.2.3 From 922edacfadf8ca0c9a13788badaf18d41db29cd1 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:26 -0600 Subject: ASoC: Intel: sof_rt5682: board id cleanup for mtl boards Many board configs are duplicated since codec and amplifier type are removed from board quirk. Introduce "mtl_rt5682_def" board to reduce the number of mtl board configs. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 9 +-------- sound/soc/intel/common/soc-acpi-intel-mtl-match.c | 4 ++-- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 995372091387..fc2582045598 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -889,14 +889,7 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_NUM_HDMIDEV(3)), }, { - .name = "mtl_rt1019_rt5682", - .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3)), - }, - { - .name = "mtl_rt5650", + .name = "mtl_rt5682_def", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(2) | SOF_RT5682_SSP_AMP(0) | diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 23eaf47b3f24..e9a5da079089 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -62,7 +62,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { }, { .comp_ids = &mtl_rt5682_rt5682s_hp, - .drv_name = "mtl_rt1019_rt5682", + .drv_name = "mtl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mtl_rt1019p_amp, .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg", @@ -84,7 +84,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { }, { .id = "10EC5650", - .drv_name = "mtl_rt5650", + .drv_name = "mtl_rt5682_def", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mtl_rt5650_amp, .sof_tplg_filename = "sof-mtl-rt5650.tplg", -- cgit v1.2.3 From 7a2a8730d51f95b263a1e8b888598dc6395220dc Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:27 -0600 Subject: ASoC: Intel: sof_rt5682: dmi quirk cleanup for mtl boards Some dmi quirks are duplicated since codec and amplifier type are removed from board quirk. Remove redundant quirks. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 40 ------------------------------------- 1 file changed, 40 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index fc2582045598..640d17c6cd35 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -154,46 +154,6 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_DISCRETE_I2S_BT"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) | - SOF_BT_OFFLOAD_SSP(1) | - SOF_SSP_BT_OFFLOAD_PRESENT - ), - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-ALC1019_ALC5682I_I2S"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(2) | - SOF_RT5682_SSP_AMP(0) | - SOF_RT5682_NUM_HDMIDEV(3) - ), - }, { .callback = sof_rt5682_quirk_cb, .matches = { -- cgit v1.2.3 From fff04329ac4bd21951d65f29934c15ff7e4b03a1 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:28 -0600 Subject: ASoC: Intel: board_helpers: support DAI link order customization Add an new field link_order_overwrite to sof_card_private structure to support machine drivers which DAI link order is different from the order used in sof_rt5682 (i.e. GLK boards or no-codec boards). Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_board_helpers.c | 231 ++++++++++++++++++----------- sound/soc/intel/boards/sof_board_helpers.h | 26 ++++ 2 files changed, 170 insertions(+), 87 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c index 4f2cb8e52971..25f9ff12618c 100644 --- a/sound/soc/intel/boards/sof_board_helpers.c +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -73,6 +73,16 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) /* * DAI Link Helpers */ + +/* DEFAULT_LINK_ORDER: the order used in sof_rt5682 */ +#define DEFAULT_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_CODEC, \ + SOF_LINK_DMIC01, \ + SOF_LINK_DMIC16K, \ + SOF_LINK_IDISP_HDMI, \ + SOF_LINK_AMP, \ + SOF_LINK_BT_OFFLOAD, \ + SOF_LINK_HDMI_IN) + static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -416,6 +426,7 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, int idx = 0; int ret; int ssp_hdmi_in = 0; + unsigned long link_order, link; num_links = calculate_num_links(ctx); @@ -424,94 +435,140 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, if (!links) return -ENOMEM; - /* headphone codec */ - if (ctx->codec_type != CODEC_NONE) { - ret = sof_intel_board_set_codec_link(dev, &links[idx], idx, - ctx->codec_type, - ctx->ssp_codec); - if (ret) { - dev_err(dev, "fail to set codec link, ret %d\n", ret); - return ret; - } - - ctx->codec_link = &links[idx]; - idx++; - } - - /* dmic01 and dmic16k */ - if (ctx->dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, - SOF_DMIC_01); - if (ret) { - dev_err(dev, "fail to set dmic01 link, ret %d\n", ret); - return ret; - } - - idx++; - } - - if (ctx->dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, - SOF_DMIC_16K); - if (ret) { - dev_err(dev, "fail to set dmic16k link, ret %d\n", ret); - return ret; - } - - idx++; - } - - /* idisp HDMI */ - for (i = 1; i <= ctx->hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx, - i, - ctx->hdmi.idisp_codec); - if (ret) { - dev_err(dev, "fail to set hdmi link, ret %d\n", ret); - return ret; + if (ctx->link_order_overwrite) + link_order = ctx->link_order_overwrite; + else + link_order = DEFAULT_LINK_ORDER; + + dev_dbg(dev, "create dai links, link_order 0x%lx\n", link_order); + + while (link_order) { + link = link_order & SOF_LINK_ORDER_MASK; + link_order >>= SOF_LINK_ORDER_SHIFT; + + switch (link) { + case SOF_LINK_CODEC: + /* headphone codec */ + if (ctx->codec_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_codec_link(dev, &links[idx], + idx, + ctx->codec_type, + ctx->ssp_codec); + if (ret) { + dev_err(dev, "fail to set codec link, ret %d\n", + ret); + return ret; + } + + ctx->codec_link = &links[idx]; + idx++; + break; + case SOF_LINK_DMIC01: + /* dmic01 */ + if (ctx->dmic_be_num == 0) + continue; + + /* at least we have dmic01 */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_01); + if (ret) { + dev_err(dev, "fail to set dmic01 link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_DMIC16K: + /* dmic16k */ + if (ctx->dmic_be_num <= 1) + continue; + + /* set up 2 BE links at most */ + ret = sof_intel_board_set_dmic_link(dev, &links[idx], + idx, SOF_DMIC_16K); + if (ret) { + dev_err(dev, "fail to set dmic16k link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_IDISP_HDMI: + /* idisp HDMI */ + for (i = 1; i <= ctx->hdmi_num; i++) { + ret = sof_intel_board_set_intel_hdmi_link(dev, + &links[idx], + idx, i, + ctx->hdmi.idisp_codec); + if (ret) { + dev_err(dev, "fail to set hdmi link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_AMP: + /* speaker amp */ + if (ctx->amp_type == CODEC_NONE) + continue; + + ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], + idx, + ctx->amp_type, + ctx->ssp_amp); + if (ret) { + dev_err(dev, "fail to set amp link, ret %d\n", + ret); + return ret; + } + + ctx->amp_link = &links[idx]; + idx++; + break; + case SOF_LINK_BT_OFFLOAD: + /* BT audio offload */ + if (!ctx->bt_offload_present) + continue; + + ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, + ctx->ssp_bt); + if (ret) { + dev_err(dev, "fail to set bt link, ret %d\n", + ret); + return ret; + } + + idx++; + break; + case SOF_LINK_HDMI_IN: + /* HDMI-In */ + for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { + ret = sof_intel_board_set_hdmi_in_link(dev, + &links[idx], + idx, + ssp_hdmi_in); + if (ret) { + dev_err(dev, "fail to set hdmi-in link, ret %d\n", + ret); + return ret; + } + + idx++; + } + break; + case SOF_LINK_NONE: + /* caught here if it's not used as terminator in macro */ + fallthrough; + default: + dev_err(dev, "invalid link type %ld\n", link); + return -EINVAL; } - - idx++; - } - - /* speaker amp */ - if (ctx->amp_type != CODEC_NONE) { - ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx, - ctx->amp_type, - ctx->ssp_amp); - if (ret) { - dev_err(dev, "fail to set amp link, ret %d\n", ret); - return ret; - } - - ctx->amp_link = &links[idx]; - idx++; - } - - /* BT audio offload */ - if (ctx->bt_offload_present) { - ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, - ctx->ssp_bt); - if (ret) { - dev_err(dev, "fail to set bt link, ret %d\n", ret); - return ret; - } - - idx++; - } - - /* HDMI-In */ - for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { - ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx, - ssp_hdmi_in); - if (ret) { - dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret); - return ret; - } - - idx++; } if (idx != num_links) { diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index 3b36058118ca..c5d6e7bec5d4 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -10,6 +10,29 @@ #include "sof_hdmi_common.h" #include "sof_ssp_common.h" +enum { + SOF_LINK_NONE = 0, + SOF_LINK_CODEC, + SOF_LINK_DMIC01, + SOF_LINK_DMIC16K, + SOF_LINK_IDISP_HDMI, + SOF_LINK_AMP, + SOF_LINK_BT_OFFLOAD, + SOF_LINK_HDMI_IN, +}; + +#define SOF_LINK_ORDER_MASK (0xF) +#define SOF_LINK_ORDER_SHIFT (4) + +#define SOF_LINK_ORDER(k1, k2, k3, k4, k5, k6, k7) \ + ((((k1) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 0)) | \ + (((k2) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 1)) | \ + (((k3) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 2)) | \ + (((k4) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 3)) | \ + (((k5) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 4)) | \ + (((k6) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 5)) | \ + (((k7) & SOF_LINK_ORDER_MASK) << (SOF_LINK_ORDER_SHIFT * 6))) + /* * sof_rt5682_private: private data for rt5682 machine driver * @@ -37,6 +60,7 @@ struct sof_rt5682_private { * @bt_offload_present: true to create BT offload BE link * @codec_link: pointer to headset codec dai link * @amp_link: pointer to speaker amplifier dai link + * @link_order_overwrite: custom DAI link order * @rt5682: private data for rt5682 machine driver */ struct sof_card_private { @@ -59,6 +83,8 @@ struct sof_card_private { struct snd_soc_dai_link *codec_link; struct snd_soc_dai_link *amp_link; + unsigned long link_order_overwrite; + union { struct sof_rt5682_private rt5682; }; -- cgit v1.2.3 From 1ad55ee7b5cd6bf6b7d06dd7262a4ddcc057c8ca Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Thu, 8 Feb 2024 10:55:29 -0600 Subject: ASoC: Intel: sof_cs42l42: use common module for DAI link generation Use intel_board module to generate DAI link array and update num_links field in snd_soc_card structure. Reviewed-by: Bard Liao Signed-off-by: Brent Lu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_cs42l42.c | 236 +++++++++-------------------------- 1 file changed, 59 insertions(+), 177 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c index c2442bf8ced0..323b86c42ef9 100644 --- a/sound/soc/intel/boards/sof_cs42l42.c +++ b/sound/soc/intel/boards/sof_cs42l42.c @@ -34,25 +34,12 @@ #define SOF_CS42L42_NUM_HDMIDEV_MASK (GENMASK(9, 7)) #define SOF_CS42L42_NUM_HDMIDEV(quirk) \ (((quirk) << SOF_CS42L42_NUM_HDMIDEV_SHIFT) & SOF_CS42L42_NUM_HDMIDEV_MASK) -#define SOF_CS42L42_DAILINK_SHIFT 10 -#define SOF_CS42L42_DAILINK_MASK (GENMASK(24, 10)) -#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \ - ((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK) #define SOF_BT_OFFLOAD_PRESENT BIT(25) #define SOF_CS42L42_SSP_BT_SHIFT 26 #define SOF_CS42L42_SSP_BT_MASK (GENMASK(28, 26)) #define SOF_CS42L42_SSP_BT(quirk) \ (((quirk) << SOF_CS42L42_SSP_BT_SHIFT) & SOF_CS42L42_SSP_BT_MASK) -enum { - LINK_NONE = 0, - LINK_HP = 1, - LINK_SPK = 2, - LINK_DMIC = 3, - LINK_HDMI = 4, - LINK_BT = 5, -}; - static struct snd_soc_jack_pin jack_pins[] = { { .pin = "Headphone Jack", @@ -182,156 +169,63 @@ static struct snd_soc_dai_link_component cs42l42_component[] = { } }; -static struct snd_soc_dai_link * -sof_card_dai_links_create(struct device *dev, enum sof_ssp_codec amp_type, - int ssp_codec, int ssp_amp, int ssp_bt, - int dmic_be_num, int hdmi_num, bool idisp_codec) +static int +sof_card_dai_links_create(struct device *dev, struct snd_soc_card *card, + struct sof_card_private *ctx) { - struct snd_soc_dai_link *links; int ret; - int id = 0; - int link_seq; - int i; - - links = devm_kcalloc(dev, sof_audio_card_cs42l42.num_links, - sizeof(struct snd_soc_dai_link), GFP_KERNEL); - if (!links) - goto devm_err; - - link_seq = (sof_cs42l42_quirk & SOF_CS42L42_DAILINK_MASK) >> SOF_CS42L42_DAILINK_SHIFT; - - while (link_seq) { - int link_type = link_seq & 0x07; - - switch (link_type) { - case LINK_HP: - ret = sof_intel_board_set_codec_link(dev, &links[id], id, - CODEC_CS42L42, - ssp_codec); - if (ret) { - dev_err(dev, "fail to create hp codec dai links, ret %d\n", - ret); - goto devm_err; - } - - /* codec-specific fields */ - links[id].codecs = cs42l42_component; - links[id].num_codecs = ARRAY_SIZE(cs42l42_component); - links[id].init = sof_cs42l42_init; - links[id].exit = sof_cs42l42_exit; - links[id].ops = &sof_cs42l42_ops; - - id++; - break; - case LINK_SPK: - if (amp_type != CODEC_NONE) { - ret = sof_intel_board_set_ssp_amp_link(dev, - &links[id], - id, - amp_type, - ssp_amp); - if (ret) { - dev_err(dev, "fail to create spk amp dai links, ret %d\n", - ret); - goto devm_err; - } - - /* codec-specific fields */ - switch (amp_type) { - case CODEC_MAX98357A: - max_98357a_dai_link(&links[id]); - break; - case CODEC_MAX98360A: - max_98360a_dai_link(&links[id]); - break; - default: - dev_err(dev, "invalid amp type %d\n", - amp_type); - goto devm_err; - } - - id++; - } - break; - case LINK_DMIC: - if (dmic_be_num > 0) { - /* at least we have dmic01 */ - ret = sof_intel_board_set_dmic_link(dev, - &links[id], - id, - SOF_DMIC_01); - if (ret) { - dev_err(dev, "fail to create dmic01 link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - - if (dmic_be_num > 1) { - /* set up 2 BE links at most */ - ret = sof_intel_board_set_dmic_link(dev, - &links[id], - id, - SOF_DMIC_16K); - if (ret) { - dev_err(dev, "fail to create dmic16k link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_HDMI: - for (i = 1; i <= hdmi_num; i++) { - ret = sof_intel_board_set_intel_hdmi_link(dev, - &links[id], - id, i, - idisp_codec); - if (ret) { - dev_err(dev, "fail to create hdmi link, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_BT: - if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { - ret = sof_intel_board_set_bt_link(dev, - &links[id], id, - ssp_bt); - if (ret) { - dev_err(dev, "fail to create bt offload dai links, ret %d\n", - ret); - goto devm_err; - } - - id++; - } - break; - case LINK_NONE: - /* caught here if it's not used as terminator in macro */ - default: - dev_err(dev, "invalid link type %d\n", link_type); - goto devm_err; - } - - link_seq >>= 3; + + ret = sof_intel_board_set_dai_link(dev, card, ctx); + if (ret) + return ret; + + if (!ctx->codec_link) { + dev_err(dev, "codec link not available"); + return -EINVAL; + } + + /* codec-specific fields for headphone codec */ + ctx->codec_link->codecs = cs42l42_component; + ctx->codec_link->num_codecs = ARRAY_SIZE(cs42l42_component); + ctx->codec_link->init = sof_cs42l42_init; + ctx->codec_link->exit = sof_cs42l42_exit; + ctx->codec_link->ops = &sof_cs42l42_ops; + + if (ctx->amp_type == CODEC_NONE) + return 0; + + if (!ctx->amp_link) { + dev_err(dev, "amp link not available"); + return -EINVAL; } - return links; -devm_err: - return NULL; + /* codec-specific fields for speaker amplifier */ + switch (ctx->amp_type) { + case CODEC_MAX98357A: + max_98357a_dai_link(ctx->amp_link); + break; + case CODEC_MAX98360A: + max_98360a_dai_link(ctx->amp_link); + break; + default: + dev_err(dev, "invalid amp type %d\n", ctx->amp_type); + return -EINVAL; + } + + return 0; } +#define GLK_LINK_ORDER SOF_LINK_ORDER(SOF_LINK_AMP, \ + SOF_LINK_CODEC, \ + SOF_LINK_DMIC01, \ + SOF_LINK_IDISP_HDMI, \ + SOF_LINK_NONE, \ + SOF_LINK_NONE, \ + SOF_LINK_NONE) + static int sof_audio_probe(struct platform_device *pdev) { struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; - struct snd_soc_dai_link *dai_links; struct sof_card_private *ctx; int ret; @@ -348,6 +242,9 @@ static int sof_audio_probe(struct platform_device *pdev) if (soc_intel_is_glk()) { ctx->dmic_be_num = 1; ctx->hdmi_num = 3; + + /* overwrite the DAI link order for GLK boards */ + ctx->link_order_overwrite = GLK_LINK_ORDER; } else { ctx->dmic_be_num = 2; ctx->hdmi_num = (sof_cs42l42_quirk & SOF_CS42L42_NUM_HDMIDEV_MASK) >> @@ -371,25 +268,13 @@ static int sof_audio_probe(struct platform_device *pdev) ctx->ssp_codec = sof_cs42l42_quirk & SOF_CS42L42_SSP_CODEC_MASK; - /* compute number of dai links */ - sof_audio_card_cs42l42.num_links = 1 + ctx->dmic_be_num + ctx->hdmi_num; - - if (ctx->amp_type != CODEC_NONE) - sof_audio_card_cs42l42.num_links++; - if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) { + if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT) ctx->bt_offload_present = true; - sof_audio_card_cs42l42.num_links++; - } - - dai_links = sof_card_dai_links_create(&pdev->dev, ctx->amp_type, - ctx->ssp_codec, ctx->ssp_amp, - ctx->ssp_bt, ctx->dmic_be_num, - ctx->hdmi_num, - ctx->hdmi.idisp_codec); - if (!dai_links) - return -ENOMEM; - sof_audio_card_cs42l42.dai_link = dai_links; + /* update dai_link */ + ret = sof_card_dai_links_create(&pdev->dev, &sof_audio_card_cs42l42, ctx); + if (ret) + return ret; sof_audio_card_cs42l42.dev = &pdev->dev; @@ -409,14 +294,12 @@ static const struct platform_device_id board_ids[] = { { .name = "glk_cs4242_mx98357a", .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(2) | - SOF_CS42L42_SSP_AMP(1)) | - SOF_CS42L42_DAILINK(LINK_SPK, LINK_HP, LINK_DMIC, LINK_HDMI, LINK_NONE), + SOF_CS42L42_SSP_AMP(1)), }, { .name = "jsl_cs4242_mx98360a", .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) | - SOF_CS42L42_SSP_AMP(1)) | - SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE), + SOF_CS42L42_SSP_AMP(1)), }, { .name = "adl_mx98360a_cs4242", @@ -424,8 +307,7 @@ static const struct platform_device_id board_ids[] = { SOF_CS42L42_SSP_AMP(1) | SOF_CS42L42_NUM_HDMIDEV(4) | SOF_BT_OFFLOAD_PRESENT | - SOF_CS42L42_SSP_BT(2) | - SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)), + SOF_CS42L42_SSP_BT(2)), }, { } }; -- cgit v1.2.3 From 9f3763b3628def09440f1f0405cc127104c31634 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:30 -0600 Subject: ASoC: Intel: sof_sdw: use single rtd_init for rt_amps 2 amps can be in the same or different dai links. To handle this, the existing code implements different spk_init functions to add dapm routes for different amps. However, sof_sdw.c doesn't support non-aggregated amp any more since it used pre-defined BE id. With that assumption, combine the spk_init functions together. This is a preparation of putting different types amps in a single dai link. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt_amp.c | 54 ++++++--------------------------- 1 file changed, 10 insertions(+), 44 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index 436975b6bdc1..a4414c9793b4 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_amp.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -185,12 +185,14 @@ static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_ return rt1318_map; } -static int first_spk_init(struct snd_soc_pcm_runtime *rtd) +static int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; const struct snd_soc_dapm_route *rt_amp_map; char codec_name[CODEC_NAME_SIZE]; + struct snd_soc_dai *dai; int ret; + int i; rt_amp_map = get_codec_name_and_route(rtd, codec_name); @@ -214,40 +216,16 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); - if (ret) - dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); - - return ret; -} - -static int second_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - const struct snd_soc_dapm_route *rt_amp_map; - char codec_name[CODEC_NAME_SIZE]; - int ret; - - rt_amp_map = get_codec_name_and_route(rtd, codec_name); - - ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); - if (ret) - dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); + for_each_rtd_codec_dais(rtd, i, dai) { + if (strstr(dai->component->name_prefix, "-1")) + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); + else if (strstr(dai->component->name_prefix, "-2")) + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); + } return ret; } -static int all_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret; - - ret = first_spk_init(rtd); - if (ret) - return ret; - - return second_spk_init(rtd); -} - static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -317,8 +295,7 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, return 0; info->amp_num++; - if (info->amp_num == 1) - dai_links->init = first_spk_init; + dai_links->init = rt_amp_spk_rtd_init; if (info->amp_num == 2) { sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); @@ -342,17 +319,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, return ret; } ctx->amp_dev2 = sdw_dev2; - - /* - * if two amps are in one dai link, the init function - * in this dai link will be first set for the first speaker, - * and it should be reset to initialize all speakers when - * the second speaker is found. - */ - if (dai_links->init) - dai_links->init = all_spk_init; - else - dai_links->init = second_spk_init; } return 0; -- cgit v1.2.3 From 4ca5ba58f15ae5a9ad1fa7a5f0d0e50b03b36614 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:31 -0600 Subject: ASoC: Intel: add get_codec_dai_by_name helper function Currently, we assume the codecs in a dai link are all the same. So that we get codec dai with snd_soc_rtd_to_codec(rtd, 0) in dai_links ->init callback. However, a link can include different codecs. For example, a 4 speakers link can consist of rt712 and rt1316. Therefore, we need to select the codec dai by name in the dai link. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_board_helpers.c | 18 ++++++++++++++++++ sound/soc/intel/boards/sof_board_helpers.h | 3 +++ 2 files changed, 21 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c index 25f9ff12618c..9c08d3e54e3b 100644 --- a/sound/soc/intel/boards/sof_board_helpers.c +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -584,6 +584,24 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, } EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); +struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, + const char *dai_name[], int num_dais) +{ + struct snd_soc_dai *dai; + int index; + int i; + + for (index = 0; index < num_dais; index++) + for_each_rtd_codec_dais(rtd, i, dai) + if (strstr(dai->name, dai_name[index])) { + dev_dbg(rtd->card->dev, "get dai %s\n", dai->name); + return dai; + } + + return NULL; +} +EXPORT_SYMBOL_NS(get_codec_dai_by_name, SND_SOC_INTEL_SOF_BOARD_HELPERS); + MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers"); MODULE_AUTHOR("Brent Lu "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index c5d6e7bec5d4..b626198f685d 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -118,4 +118,7 @@ int sof_intel_board_set_hdmi_in_link(struct device *dev, struct snd_soc_dai_link *link, int be_id, int ssp_hdmi); +struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, + const char *dai_name[], int num_dais); + #endif /* __SOF_INTEL_BOARD_HELPERS_H */ -- cgit v1.2.3 From 49f679a175b4fbdea88ba8787c22bce90c60565b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:32 -0600 Subject: ASoC: Intel: sof_sdw_rt_sdca_jack_common: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-12-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 1 + sound/soc/intel/boards/sof_board_helpers.c | 2 +- sound/soc/intel/boards/sof_board_helpers.h | 2 +- sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c | 15 +++++++++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 8fd5e7f83054..18ac3ce0752e 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -677,6 +677,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES || COMPILE_TEST depends on SOUNDWIRE + select SND_SOC_INTEL_SOF_BOARD_HELPERS select SND_SOC_MAX98363 select SND_SOC_MAX98373_I2C select SND_SOC_MAX98373_SDW diff --git a/sound/soc/intel/boards/sof_board_helpers.c b/sound/soc/intel/boards/sof_board_helpers.c index 9c08d3e54e3b..088894ff4165 100644 --- a/sound/soc/intel/boards/sof_board_helpers.c +++ b/sound/soc/intel/boards/sof_board_helpers.c @@ -585,7 +585,7 @@ int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, - const char *dai_name[], int num_dais) + const char * const dai_name[], int num_dais) { struct snd_soc_dai *dai; int index; diff --git a/sound/soc/intel/boards/sof_board_helpers.h b/sound/soc/intel/boards/sof_board_helpers.h index b626198f685d..f42d5d640321 100644 --- a/sound/soc/intel/boards/sof_board_helpers.h +++ b/sound/soc/intel/boards/sof_board_helpers.h @@ -119,6 +119,6 @@ int sof_intel_board_set_hdmi_in_link(struct device *dev, int ssp_hdmi); struct snd_soc_dai *get_codec_dai_by_name(struct snd_soc_pcm_runtime *rtd, - const char *dai_name[], int num_dais); + const char * const dai_name[], int num_dais); #endif /* __SOF_INTEL_BOARD_HELPERS_H */ diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index d9c283829fc7..4f2e105a1124 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -15,6 +15,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" /* @@ -84,15 +85,24 @@ static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { }, }; +static const char * const jack_codecs[] = { + "rt711", "rt712", "rt713" +}; + static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:%s-sdca", card->components, component->name_prefix); @@ -213,3 +223,4 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 91a959d8913e3f2d3c3baed0a8469f878c838ff2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:33 -0600 Subject: ASoC: Intel: sof_sdw_rt711: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt711.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index 38782fdfcf15..5d8f90f2bf55 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -15,6 +15,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" /* @@ -69,15 +70,24 @@ static struct snd_soc_jack_pin rt711_jack_pins[] = { }, }; +static const char * const jack_codecs[] = { + "rt711" +}; + static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt711", card->components); @@ -180,3 +190,4 @@ int sof_sdw_rt711_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From c44f69bbcc7f0f4fd17ecc9ba13f9a91a6b5ccec Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:34 -0600 Subject: ASoC: Intel: sof_sdw_rt712_sdca: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt712_sdca.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c index 3092029419df..27c924885ffc 100644 --- a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c @@ -13,6 +13,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt712_spk_widgets[] = { @@ -77,12 +78,21 @@ int sof_sdw_rt712_spk_init(struct snd_soc_card *card, return 0; } +static const char * const dmics[] = { + "rt712-sdca-dmic" +}; + static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; + + codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics)); + if (!codec_dai) + return -EINVAL; + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s mic:%s", card->components, component->name_prefix); @@ -102,3 +112,4 @@ int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 3e522c9852bc22ee4c257062fa6d57b4dd6b0f61 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:35 -0600 Subject: ASoC: Intel: sof_sdw_rt700: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt700.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index a1714afe8515..d9a45392bbbf 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -13,6 +13,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt700_widgets[] = { @@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt700_jack_pins[] = { }, }; +static const char * const jack_codecs[] = { + "rt700" +}; + static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt700", card->components); @@ -127,3 +137,4 @@ int sof_sdw_rt700_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 5e052fba621c2c57172fc6a1a9d73692fcc6d06d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:36 -0600 Subject: ASoC: Intel: sof_sdw_cs42l42: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-16-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_cs42l42.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index 436f41086da6..22f4f9a19088 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -15,6 +15,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget cs42l42_widgets[] = { @@ -46,15 +47,24 @@ static struct snd_soc_jack_pin cs42l42_jack_pins[] = { }, }; +static const char * const jack_codecs[] = { + "cs42l42" +}; + static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:cs42l42", card->components); @@ -129,3 +139,4 @@ int sof_sdw_cs42l42_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 7bc6ceba7d354564d6b49d23830fa9d366e8ed31 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:37 -0600 Subject: ASoC: Intel: sof_sdw_rt5682: use helper to get codec dai by name Use helper to get codec dai by name. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt5682.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 7b7c9def398b..27aca76dbee4 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -15,6 +15,7 @@ #include #include #include +#include "sof_board_helpers.h" #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget rt5682_widgets[] = { @@ -45,15 +46,24 @@ static struct snd_soc_jack_pin rt5682_jack_pins[] = { }, }; +static const char * const jack_codecs[] = { + "rt5682" +}; + static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); - struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dai *codec_dai; + struct snd_soc_component *component; struct snd_soc_jack *jack; int ret; + codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s hs:rt5682", card->components); @@ -128,3 +138,4 @@ int sof_sdw_rt5682_init(struct snd_soc_card *card, return 0; } +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 8266c73126b75eabbebefe7ce489a798e9ef2662 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:38 -0600 Subject: ASoC: Intel: sof_sdw: add common sdw dai link init Currently, we set sdw dai link .init callback in the codec_info_list's dais.init function. This works fine if all codecs in the dai link are the same. However, we need to do all the .init stuff for all different codecs in the dai link if not all codecs in the dai link are the same. Use a common dai link .init callback to call the new rtd_init callback in sof_sdw_dai_info{} to do rtd_init for each dai. Some codec init callback will become empty after this change. They will be removed in the follow up patch. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-18-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 75 +++++++++++++++++++++- sound/soc/intel/boards/sof_sdw_common.h | 20 ++++++ sound/soc/intel/boards/sof_sdw_cs42l42.c | 4 +- sound/soc/intel/boards/sof_sdw_cs42l43.c | 7 +- sound/soc/intel/boards/sof_sdw_cs_amp.c | 3 +- sound/soc/intel/boards/sof_sdw_maxim.c | 4 +- sound/soc/intel/boards/sof_sdw_rt5682.c | 4 +- sound/soc/intel/boards/sof_sdw_rt700.c | 4 +- sound/soc/intel/boards/sof_sdw_rt711.c | 4 +- sound/soc/intel/boards/sof_sdw_rt712_sdca.c | 8 +-- sound/soc/intel/boards/sof_sdw_rt715.c | 4 +- sound/soc/intel/boards/sof_sdw_rt715_sdca.c | 4 +- sound/soc/intel/boards/sof_sdw_rt_amp.c | 3 +- .../soc/intel/boards/sof_sdw_rt_sdca_jack_common.c | 4 +- 14 files changed, 108 insertions(+), 40 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 300391fbc2fc..782b45adb21e 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -651,6 +651,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt700_init, + .rtd_init = rt700_rtd_init, }, }, .dai_num = 1, @@ -666,6 +667,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, }, .dai_num = 1, @@ -681,6 +683,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, + .rtd_init = rt711_rtd_init, }, }, .dai_num = 1, @@ -696,6 +699,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, { .direction = {true, false}, @@ -703,6 +707,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_rt712_spk_init, + .rtd_init = rt712_spk_rtd_init, }, }, .dai_num = 2, @@ -717,6 +722,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt712_sdca_dmic_init, + .rtd_init = rt712_sdca_dmic_rtd_init, }, }, .dai_num = 1, @@ -732,6 +738,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt_sdca_jack_init, .exit = sof_sdw_rt_sdca_jack_exit, + .rtd_init = rt_sdca_jack_rtd_init, }, }, .dai_num = 1, @@ -746,6 +753,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt712_sdca_dmic_init, + .rtd_init = rt712_sdca_dmic_rtd_init, }, }, .dai_num = 1, @@ -761,6 +769,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -776,6 +785,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -790,6 +800,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_rt_amp_init, .exit = sof_sdw_rt_amp_exit, + .rtd_init = rt_amp_spk_rtd_init, }, }, .dai_num = 1, @@ -805,6 +816,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt715_sdca_init, + .rtd_init = rt715_sdca_rtd_init, }, }, .dai_num = 1, @@ -820,6 +832,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt715_sdca_init, + .rtd_init = rt715_sdca_rtd_init, }, }, .dai_num = 1, @@ -835,6 +848,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt715_init, + .rtd_init = rt715_rtd_init, }, }, .dai_num = 1, @@ -850,6 +864,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_rt715_init, + .rtd_init = rt715_rtd_init, }, }, .dai_num = 1, @@ -893,6 +908,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_maxim_init, + .rtd_init = maxim_spk_rtd_init, }, }, .dai_num = 1, @@ -906,6 +922,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_maxim_init, + .rtd_init = maxim_spk_rtd_init, }, }, .dai_num = 1, @@ -919,6 +936,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_rt5682_init, + .rtd_init = rt5682_rtd_init, }, }, .dai_num = 1, @@ -932,6 +950,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = sof_sdw_cs_amp_init, + .rtd_init = cs_spk_rtd_init, }, }, .dai_num = 1, @@ -945,6 +964,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, .init = sof_sdw_cs42l42_init, + .rtd_init = cs42l42_rtd_init, }, }, .dai_num = 1, @@ -959,6 +979,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, .init = sof_sdw_cs42l43_hs_init, + .rtd_init = cs42l43_hs_rtd_init, }, { .direction = {false, true}, @@ -966,6 +987,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, .init = sof_sdw_cs42l43_dmic_init, + .rtd_init = cs42l43_dmic_rtd_init, }, { .direction = {false, true}, @@ -1387,6 +1409,56 @@ static void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps, } } +static inline int find_codec_info_dai(const char *dai_name, int *dai_index) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { + for (j = 0; j < codec_info_list[i].dai_num; j++) { + if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) { + *dai_index = j; + return i; + } + } + } + + return -EINVAL; +} + +static int sof_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sof_sdw_codec_info *codec_info; + struct snd_soc_dai *dai; + int codec_index; + int dai_index; + int ret; + int i; + + for_each_rtd_codec_dais(rtd, i, dai) { + codec_index = find_codec_info_dai(dai->name, &dai_index); + if (codec_index < 0) + return -EINVAL; + + codec_info = &codec_info_list[codec_index]; + /* + * A codec dai can be connected to different dai links for capture and playback, + * but we only need to call the rtd_init function once. + * The rtd_init for each codec dai is independent. So, the order of rtd_init + * doesn't matter. + */ + if (codec_info->dais[dai_index].rtd_init_done) + continue; + if (codec_info->dais[dai_index].rtd_init) { + ret = codec_info->dais[dai_index].rtd_init(rtd); + if (ret) + return ret; + } + codec_info->dais[dai_index].rtd_init_done = true; + } + + return 0; +} + static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, @@ -1547,7 +1619,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, init_dai_link(dev, dai_links + *link_index, be_id, name, playback, capture, cpus, cpu_dai_num, codecs, codec_num, - NULL, &sdw_ops); + sof_sdw_rtd_init, &sdw_ops); /* * SoundWire DAILINKs use 'stream' functions and Bank Switch operations @@ -1880,6 +1952,7 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { for (j = 0; j < codec_info_list[i].dai_num; j++) { + codec_info_list[i].dais[j].rtd_init_done = false; /* Check each dai in codec_info_lis to see if it is used in the link */ if (!codec_info_list[i].dais[j].exit) continue; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index f16456945edb..ab444dae46ab 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -78,6 +78,8 @@ struct sof_sdw_dai_info { struct sof_sdw_codec_info *info, bool playback); int (*exit)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); + int (*rtd_init)(struct snd_soc_pcm_runtime *rtd); + bool rtd_init_done; /* Indicate that the rtd_init callback is done */ }; struct sof_sdw_codec_info { @@ -235,4 +237,22 @@ int sof_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); + +/* dai_link init callbacks */ + +int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); +int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd); + #endif diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index 22f4f9a19088..5d0915b72c7f 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -51,7 +51,7 @@ static const char * const jack_codecs[] = { "cs42l42" }; -static int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) +int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -135,8 +135,6 @@ int sof_sdw_cs42l42_init(struct snd_soc_card *card, if (!playback) return 0; - dai_links->init = cs42l42_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_cs42l43.c b/sound/soc/intel/boards/sof_sdw_cs42l43.c index 360f11b72aa2..7909ea9c9c14 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l43.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l43.c @@ -50,7 +50,7 @@ static struct snd_soc_jack_pin sof_jack_pins[] = { }, }; -static int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) +int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component; struct mc_private *ctx = snd_soc_card_get_drvdata(rtd->card); @@ -116,12 +116,11 @@ int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, const struct snd_soc_acpi * No need to test if (!playback) like other codecs as cs42l43 uses separated dai for * playback and capture, and sof_sdw_cs42l43_init is only linked to the playback dai. */ - dai_links->init = cs42l43_hs_rtd_init; return 0; } -static int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -150,7 +149,5 @@ int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, const struct snd_soc_ac struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback) { - dai_links->init = cs42l43_dmic_rtd_init; - return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c index f88c01552a92..56cf75bc6cc4 100644 --- a/sound/soc/intel/boards/sof_sdw_cs_amp.c +++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c @@ -18,7 +18,7 @@ static const struct snd_soc_dapm_widget sof_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), }; -static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) +int cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { const char *dai_name = rtd->dai_link->codecs->dai_name; struct snd_soc_card *card = rtd->card; @@ -67,7 +67,6 @@ int sof_sdw_cs_amp_init(struct snd_soc_card *card, return 0; info->amp_num++; - dai_links->init = cs_spk_init; return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_maxim.c b/sound/soc/intel/boards/sof_sdw_maxim.c index e36b8d8c70c9..034730432671 100644 --- a/sound/soc/intel/boards/sof_sdw_maxim.c +++ b/sound/soc/intel/boards/sof_sdw_maxim.c @@ -27,7 +27,7 @@ static const struct snd_kcontrol_new maxim_controls[] = { SOC_DAPM_PIN_SWITCH("Right Spk"), }; -static int spk_init(struct snd_soc_pcm_runtime *rtd) +int maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -145,8 +145,6 @@ int sof_sdw_maxim_init(struct snd_soc_card *card, bool playback) { info->amp_num++; - if (info->amp_num == 2) - dai_links->init = spk_init; maxim_part_id = info->part_id; switch (maxim_part_id) { diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 27aca76dbee4..4e3fcc861074 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -50,7 +50,7 @@ static const char * const jack_codecs[] = { "rt5682" }; -static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -134,8 +134,6 @@ int sof_sdw_rt5682_init(struct snd_soc_card *card, if (!playback) return 0; - dai_links->init = rt5682_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index d9a45392bbbf..781d41e35191 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -50,7 +50,7 @@ static const char * const jack_codecs[] = { "rt700" }; -static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -133,8 +133,6 @@ int sof_sdw_rt700_init(struct snd_soc_card *card, if (!playback) return 0; - dai_links->init = rt700_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index 5d8f90f2bf55..cdd1587b246c 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -74,7 +74,7 @@ static const char * const jack_codecs[] = { "rt711" }; -static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -186,8 +186,6 @@ int sof_sdw_rt711_init(struct snd_soc_card *card, } ctx->headset_codec_dev = sdw_dev; - dai_links->init = rt711_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c index 27c924885ffc..dddb27e4c943 100644 --- a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c @@ -35,7 +35,7 @@ static const struct snd_kcontrol_new rt712_spk_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -static int rt712_spk_init(struct snd_soc_pcm_runtime *rtd) +int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; int ret; @@ -73,8 +73,6 @@ int sof_sdw_rt712_spk_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - dai_links->init = rt712_spk_init; - return 0; } @@ -82,7 +80,7 @@ static const char * const dmics[] = { "rt712-sdca-dmic" }; -static int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct snd_soc_dai *codec_dai; @@ -108,8 +106,6 @@ int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - dai_links->init = rt712_sdca_dmic_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index 7c068dc6b9cf..19194fe92b8e 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -11,7 +11,7 @@ #include #include "sof_sdw_common.h" -static int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -30,7 +30,5 @@ int sof_sdw_rt715_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - dai_links->init = rt715_rtd_init; - return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index ca0cf3db2e4d..3089fa8450fa 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -11,7 +11,7 @@ #include #include "sof_sdw_common.h" -static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -30,7 +30,5 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - dai_links->init = rt715_sdca_rtd_init; - return 0; } diff --git a/sound/soc/intel/boards/sof_sdw_rt_amp.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c index a4414c9793b4..202edab95000 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_amp.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -185,7 +185,7 @@ static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_ return rt1318_map; } -static int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; const struct snd_soc_dapm_route *rt_amp_map; @@ -295,7 +295,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, return 0; info->amp_num++; - dai_links->init = rt_amp_spk_rtd_init; if (info->amp_num == 2) { sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); diff --git a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c index 4f2e105a1124..5253d8332780 100644 --- a/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c +++ b/sound/soc/intel/boards/sof_sdw_rt_sdca_jack_common.c @@ -89,7 +89,7 @@ static const char * const jack_codecs[] = { "rt711", "rt712", "rt713" }; -static int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) +int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -219,8 +219,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, } ctx->headset_codec_dev = sdw_dev; - dai_links->init = rt_sdca_jack_rtd_init; - return 0; } MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 579d6596ebea488ad661bfa484c771c2b47eecc5 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:39 -0600 Subject: ASoC: Intel: sof_sdw: remove .init callbacks Some codec .init callbacks are empty after removing dai_links->init = xxx_rtd_init;. Remove those callbacks. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-19-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 12 ------------ sound/soc/intel/boards/sof_sdw_cs42l42.c | 16 ---------------- sound/soc/intel/boards/sof_sdw_cs42l43.c | 18 ------------------ sound/soc/intel/boards/sof_sdw_rt5682.c | 16 ---------------- sound/soc/intel/boards/sof_sdw_rt700.c | 16 ---------------- sound/soc/intel/boards/sof_sdw_rt712_sdca.c | 18 ------------------ sound/soc/intel/boards/sof_sdw_rt715.c | 8 -------- sound/soc/intel/boards/sof_sdw_rt715_sdca.c | 8 -------- 8 files changed, 112 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 782b45adb21e..801cfe9c4dd3 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -650,7 +650,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt700-aif1", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_rt700_init, .rtd_init = rt700_rtd_init, }, }, @@ -706,7 +705,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-aif2", .dai_type = SOF_SDW_DAI_TYPE_AMP, .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, - .init = sof_sdw_rt712_spk_init, .rtd_init = rt712_spk_rtd_init, }, }, @@ -721,7 +719,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt712_sdca_dmic_init, .rtd_init = rt712_sdca_dmic_rtd_init, }, }, @@ -752,7 +749,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt712_sdca_dmic_init, .rtd_init = rt712_sdca_dmic_rtd_init, }, }, @@ -815,7 +811,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_sdca_init, .rtd_init = rt715_sdca_rtd_init, }, }, @@ -831,7 +826,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_sdca_init, .rtd_init = rt715_sdca_rtd_init, }, }, @@ -847,7 +841,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_init, .rtd_init = rt715_rtd_init, }, }, @@ -863,7 +856,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_rt715_init, .rtd_init = rt715_rtd_init, }, }, @@ -935,7 +927,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt5682-sdw", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_rt5682_init, .rtd_init = rt5682_rtd_init, }, }, @@ -963,7 +954,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "cs42l42-sdw", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, - .init = sof_sdw_cs42l42_init, .rtd_init = cs42l42_rtd_init, }, }, @@ -978,7 +968,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "cs42l43-dp5", .dai_type = SOF_SDW_DAI_TYPE_JACK, .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, - .init = sof_sdw_cs42l43_hs_init, .rtd_init = cs42l43_hs_rtd_init, }, { @@ -986,7 +975,6 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "cs42l43-dp1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .init = sof_sdw_cs42l43_dmic_init, .rtd_init = cs42l43_dmic_rtd_init, }, { diff --git a/sound/soc/intel/boards/sof_sdw_cs42l42.c b/sound/soc/intel/boards/sof_sdw_cs42l42.c index 5d0915b72c7f..0dc297f7de01 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l42.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l42.c @@ -121,20 +121,4 @@ int cs42l42_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_cs42l42_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - return 0; -} MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_cs42l43.c b/sound/soc/intel/boards/sof_sdw_cs42l43.c index 7909ea9c9c14..a9b6edac2ecd 100644 --- a/sound/soc/intel/boards/sof_sdw_cs42l43.c +++ b/sound/soc/intel/boards/sof_sdw_cs42l43.c @@ -108,18 +108,6 @@ int cs42l43_hs_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * No need to test if (!playback) like other codecs as cs42l43 uses separated dai for - * playback and capture, and sof_sdw_cs42l43_init is only linked to the playback dai. - */ - - return 0; -} - int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -145,9 +133,3 @@ int cs42l43_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, - bool playback) -{ - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index 4e3fcc861074..6b008a5a343b 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -120,20 +120,4 @@ int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_rt5682_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - return 0; -} MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index 781d41e35191..88e785a54b16 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -119,20 +119,4 @@ int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - -int sof_sdw_rt700_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * headset should be initialized once. - * Do it with dai link for playback. - */ - if (!playback) - return 0; - - return 0; -} MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c index dddb27e4c943..ebb4b58c198b 100644 --- a/sound/soc/intel/boards/sof_sdw_rt712_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt712_sdca.c @@ -67,15 +67,6 @@ int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } -int sof_sdw_rt712_spk_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - return 0; -} - static const char * const dmics[] = { "rt712-sdca-dmic" }; @@ -99,13 +90,4 @@ int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } - -int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - return 0; -} MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index 19194fe92b8e..b5a886cd595d 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -24,11 +24,3 @@ int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } -int sof_sdw_rt715_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index 3089fa8450fa..4b37a8a6dd2e 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -24,11 +24,3 @@ int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) return 0; } -int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - return 0; -} -- cgit v1.2.3 From c13e03126a5be90781084437689724254c8226e1 Mon Sep 17 00:00:00 2001 From: mosomate Date: Thu, 8 Feb 2024 10:55:40 -0600 Subject: ASoC: Intel: common: DMI remap for rebranded Intel NUC M15 (LAPRC710) laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added DMI quirk to handle the rebranded variants of Intel NUC M15 (LAPRC710) laptops. The DMI matching is based on motherboard attributes. Link: https://github.com/thesofproject/linux/issues/4218 Signed-off-by: Máté Mosonyi Reviewed-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-20-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- drivers/soundwire/dmi-quirks.c | 8 ++++++++ sound/soc/intel/boards/sof_sdw.c | 11 +++++++++++ 2 files changed, 19 insertions(+) (limited to 'sound/soc') diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 9ebdd0cd0b1c..91ab97a456fa 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -130,6 +130,14 @@ static const struct dmi_system_id adr_remap_quirk_table[] = { }, .driver_data = (void *)intel_rooks_county, }, + { + /* quirk used for NUC15 LAPRC710 skew */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), + }, + .driver_data = (void *)intel_rooks_county, + }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 801cfe9c4dd3..e4b9f4d1ec06 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -236,6 +236,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { SOF_SDW_PCH_DMIC | RT711_JD2_100K), }, + { + /* NUC15 LAPRC710 skews */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + SOF_SDW_PCH_DMIC | + RT711_JD2_100K), + }, /* TigerLake-SDCA devices */ { .callback = sof_sdw_quirk_cb, -- cgit v1.2.3 From c1469c3a8a306e5f1eab1ae9585640d08e183f87 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 8 Feb 2024 10:55:41 -0600 Subject: ASoC: Intel: ssp-common: Add stub for sof_ssp_get_codec_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As this function is now used in sof_board_helpers it requires a build stub for the case SSP_COMMON is not built in. Fixes: ba0c7c328762 ("ASoC: Intel: board_helpers: support amp link initialization") Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Signed-off-by: Charles Keepax Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-21-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_ssp_common.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_ssp_common.h b/sound/soc/intel/boards/sof_ssp_common.h index 6d827103479b..d24888bc99fd 100644 --- a/sound/soc/intel/boards/sof_ssp_common.h +++ b/sound/soc/intel/boards/sof_ssp_common.h @@ -67,6 +67,14 @@ enum sof_ssp_codec { enum sof_ssp_codec sof_ssp_detect_codec_type(struct device *dev); enum sof_ssp_codec sof_ssp_detect_amp_type(struct device *dev); + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SOF_SSP_COMMON) const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type); +#else +static inline const char *sof_ssp_get_codec_name(enum sof_ssp_codec codec_type) +{ + return NULL; +} +#endif #endif /* __SOF_SSP_COMMON_H */ -- cgit v1.2.3 From 36fe7a495e32465b3d989459c497f0acf614be47 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 8 Feb 2024 10:55:42 -0600 Subject: ASoC: Intel: sof_sdw: Remove unused function prototypes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent commits remove a lot of init functions remove their function prototypes as well. Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Signed-off-by: Charles Keepax Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-22-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_common.h | 62 --------------------------------- 1 file changed, 62 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index ab444dae46ab..b1d57034361c 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -139,25 +139,6 @@ int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, bool playback); int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); -/* RT712-SDCA support */ -int sof_sdw_rt712_spk_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); -int sof_sdw_rt712_sdca_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* RT700 support */ -int sof_sdw_rt700_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* RT1308 I2S support */ extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; @@ -169,22 +150,6 @@ int sof_sdw_rt_amp_init(struct snd_soc_card *card, bool playback); int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); -/* RT1316 support */ - -/* RT715 support */ -int sof_sdw_rt715_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* RT715-SDCA support */ -int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* RT722-SDCA support */ int sof_sdw_rt722_spk_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, @@ -204,33 +169,6 @@ int sof_sdw_maxim_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); -/* RT5682 support */ -int sof_sdw_rt5682_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* CS42L42 support */ -int sof_sdw_cs42l42_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -/* CS42L43 support */ -int sof_sdw_cs42l43_hs_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - -int sof_sdw_cs42l43_dmic_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); - /* CS AMP support */ int sof_sdw_cs_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, -- cgit v1.2.3 From 0bbb0136b4e7729f533b1b3eb805c4217086e4ce Mon Sep 17 00:00:00 2001 From: Chao Song Date: Thu, 8 Feb 2024 10:55:43 -0600 Subject: ASoC: Intel: soc-acpi: add RT712 support for LNL This patch adds RT712 support for LNL. Reviewed-by: Bard Liao Signed-off-by: Chao Song Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-23-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-lnl-match.c | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'sound/soc') 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 5897bb6b28b8..3d48e161cb33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -36,6 +36,21 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -45,6 +60,24 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_2_single_adr[] = { + { + .adr = 0x000230025D071201ull, + .num_endpoints = ARRAY_SIZE(rt712_endpoints), + .endpoints = rt712_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { + { + .adr = 0x000330025D171201ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt712-dmic" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { { .adr = 0x000230025D131601ull, @@ -81,6 +114,20 @@ static const struct snd_soc_acpi_link_adr lnl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_712_only[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt712_2_single_adr), + .adr_d = rt712_2_single_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1712_3_single_adr), + .adr_d = rt1712_3_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = { { .mask = BIT(0), @@ -138,6 +185,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt711.tplg", }, + { + .link_mask = BIT(2) | BIT(3), + .links = lnl_712_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines); -- cgit v1.2.3 From 7fa43af5b4cc78c4616d8345740203807593ed43 Mon Sep 17 00:00:00 2001 From: Chao Song Date: Thu, 8 Feb 2024 10:55:44 -0600 Subject: ASoC: Intel: soc-acpi-intel-lnl-match: Add rt722 support This patch adds match table for rt722 multiple function codec on link 0. Reviewed-by: Bard Liao Signed-off-by: Chao Song Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-24-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-lnl-match.c | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'sound/soc') 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 3d48e161cb33..74d6dcd7471f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -51,6 +51,31 @@ static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { }, }; +/* + * RT722 is a multi-function codec, three endpoints are created for + * its headset, amp and dmic functions. + */ +static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -78,6 +103,15 @@ static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { + { + .adr = 0x000030025d072201ull, + .num_endpoints = ARRAY_SIZE(rt722_endpoints), + .endpoints = rt722_endpoints, + .name_prefix = "rt722" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { { .adr = 0x000230025D131601ull, @@ -128,6 +162,15 @@ static const struct snd_soc_acpi_link_adr lnl_712_only[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_rt722_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt722_0_single_adr), + .adr_d = rt722_0_single_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_3_in_1_sdca[] = { { .mask = BIT(0), @@ -191,6 +234,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-lnl-rt712-l2-rt1712-l3.tplg", }, + { + .link_mask = BIT(0), + .links = lnl_rt722_only, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines); -- cgit v1.2.3 From 6b4c7d4d8297a9f395ff4addba8e5fde7f730c37 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 8 Feb 2024 10:55:45 -0600 Subject: ASoC: Intel: sof_sdw: starts non sdw BE id with the highest sdw BE id The soundwire links do not have their IDs as consecutive numbers, thus the last link might have lower be_id than the previous one and this leads to id collision with non SDW links. For example, create dai link SDW0-Playback-SimpleJack, id 0 create dai link SDW0-Capture-SmartMic, id 4 create dai link SDW0-Capture-SimpleJack, id 1 create dai link SDW2-Playback-SmartAmp, id 2 create dai link SDW2-Capture-SmartAmp, id 3 create dai link iDisp1, id 4 create dai link iDisp2, id 5 create dai link iDisp3, id 6 Reviewed-by: Chao Song Co-developed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240208165545.93811-25-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index e4b9f4d1ec06..08f330ed5c2e 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1767,15 +1767,21 @@ out: return codec_index; for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { + int current_be_id; + ret = create_sdw_dailink(card, &link_index, dai_links, sdw_be_num, adr_link, codec_conf, codec_conf_num, - &be_id, &codec_conf_index, + ¤t_be_id, &codec_conf_index, &ignore_pch_dmic, append_dai_type, i, j); if (ret < 0) { dev_err(dev, "failed to create dai link %d\n", link_index); return ret; } + + /* Update the be_id to match the highest ID used for SDW link */ + if (be_id < current_be_id) + be_id = current_be_id; } if (aggregation && endpoint->aggregated) -- cgit v1.2.3 From 4089d82e67a9967fc5bf2b4e5ef820d67fe73924 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Fri, 9 Feb 2024 06:59:34 +0100 Subject: ASoC: tas2781: remove unused acpi_subysystem_id The acpi_subysystem_id is only written and freed, not read, so unnecessary. Signed-off-by: Gergo Koteles Link: https://lore.kernel.org/r/454639336be28d2b50343e9c8366a56b0975e31d.1707456753.git.soyer@irl.hu Signed-off-by: Mark Brown --- include/sound/tas2781.h | 1 - sound/pci/hda/tas2781_hda_i2c.c | 12 ------------ sound/soc/codecs/tas2781-comlib.c | 1 - 3 files changed, 14 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index b00d65417c31..cc110a360861 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -103,7 +103,6 @@ struct tasdevice_priv { struct tm tm; enum device_catlog_id catlog_id; - const char *acpi_subsystem_id; unsigned char cal_binaryname[TASDEVICE_MAX_CHANNELS][64]; unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE]; unsigned char coef_binaryname[64]; diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 2dd809de62e5..08da7631fadb 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -111,9 +111,7 @@ static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct acpi_device *adev; - struct device *physdev; LIST_HEAD(resources); - const char *sub; int ret; adev = acpi_dev_get_first_match_dev(hid, NULL, -1); @@ -129,18 +127,8 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); - /* No side-effect to the playback even if subsystem_id is NULL*/ - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) - sub = NULL; - - p->acpi_subsystem_id = sub; - - put_device(physdev); - return 0; err: diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c index b7e56ceb1acf..09ed154c6ea8 100644 --- a/sound/soc/codecs/tas2781-comlib.c +++ b/sound/soc/codecs/tas2781-comlib.c @@ -407,7 +407,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv) { if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) gpio_free(tas_priv->irq_info.irq_gpio); - kfree(tas_priv->acpi_subsystem_id); mutex_destroy(&tas_priv->codec_lock); } EXPORT_SYMBOL_GPL(tasdevice_remove); -- cgit v1.2.3 From f7fc624be3dbfb78047a1cab795b93c7235fbf1c Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 9 Feb 2024 09:52:56 +0100 Subject: ASoC: Intel: avs: Expose FW version with sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add functionality to read version of loaded FW from sysfs. Signed-off-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20240209085256.121261-1-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- .../ABI/testing/sysfs-bus-pci-devices-avs | 8 +++++ sound/soc/intel/avs/Makefile | 3 +- sound/soc/intel/avs/avs.h | 4 +++ sound/soc/intel/avs/core.c | 1 + sound/soc/intel/avs/sysfs.c | 35 ++++++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-pci-devices-avs create mode 100644 sound/soc/intel/avs/sysfs.c (limited to 'sound/soc') diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-avs b/Documentation/ABI/testing/sysfs-bus-pci-devices-avs new file mode 100644 index 000000000000..ebff3fa12055 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-avs @@ -0,0 +1,8 @@ +What: /sys/devices/pci0000:00//avs/fw_version +Date: February 2024 +Contact: Cezary Rojewski +Description: + Version of AudioDSP firmware ASoC avs driver is communicating + with. + + Format: %d.%d.%d.%d, type:major:minor:build. diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 460ee6599daf..a3fad926d0fb 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ - topology.o path.o pcm.o board_selection.o control.o + topology.o path.o pcm.o board_selection.o control.o \ + sysfs.o snd-soc-avs-objs += cldma.o snd-soc-avs-objs += skl.o apl.o diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index d694e08e44e1..69c912feb8a7 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -392,4 +392,8 @@ static inline void avs_debugfs_init(struct avs_dev *adev) { } static inline void avs_debugfs_exit(struct avs_dev *adev) { } #endif +/* Filesystems integration */ + +extern const struct attribute_group *avs_attr_groups[]; + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 59c3793f65df..aa98768a7c56 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -773,6 +773,7 @@ static struct pci_driver avs_pci_driver = { .probe = avs_pci_probe, .remove = avs_pci_remove, .shutdown = avs_pci_shutdown, + .dev_groups = avs_attr_groups, .driver = { .pm = &avs_dev_pm, }, diff --git a/sound/soc/intel/avs/sysfs.c b/sound/soc/intel/avs/sysfs.c new file mode 100644 index 000000000000..cce21636fbc0 --- /dev/null +++ b/sound/soc/intel/avs/sysfs.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include "avs.h" + +static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct avs_dev *adev = to_avs_dev(dev); + struct avs_fw_version *fw_version = &adev->fw_cfg.fw_version; + + return sysfs_emit(buf, "%d.%d.%d.%d\n", fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build); +} +static DEVICE_ATTR_RO(fw_version); + +static struct attribute *avs_fw_attrs[] = { + &dev_attr_fw_version.attr, + NULL +}; + +static const struct attribute_group avs_attr_group = { + .name = "avs", + .attrs = avs_fw_attrs, +}; + +const struct attribute_group *avs_attr_groups[] = { + &avs_attr_group, + NULL +}; -- cgit v1.2.3 From 8e5ffd767bac48180235456255831083d0d00195 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Feb 2024 16:51:24 +0100 Subject: ASoC: pxa2xx-ac97: Replace with DEFINE_SIMPLE_DEV_PM_OPS() Use the new DEFINE_SIMPLE_DEV_PM_OPS() instead of SIMPLE_DEV_PM_OPS() for code-simplification. We need no longer CONFIG_PM_SLEEP ifdefs. Just a cleanup, no functional changes. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20240207155140.18238-14-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/soc/pxa/pxa2xx-ac97.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index e73bd62c033c..80e0ea0ec9fb 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -271,7 +271,6 @@ static void pxa2xx_ac97_dev_remove(struct platform_device *pdev) pxa2xx_ac97_hw_remove(pdev); } -#ifdef CONFIG_PM_SLEEP static int pxa2xx_ac97_dev_suspend(struct device *dev) { return pxa2xx_ac97_hw_suspend(); @@ -282,18 +281,15 @@ static int pxa2xx_ac97_dev_resume(struct device *dev) return pxa2xx_ac97_hw_resume(); } -static SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, +static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_dev_suspend, pxa2xx_ac97_dev_resume); -#endif static struct platform_driver pxa2xx_ac97_driver = { .probe = pxa2xx_ac97_dev_probe, .remove_new = pxa2xx_ac97_dev_remove, .driver = { .name = "pxa2xx-ac97", -#ifdef CONFIG_PM_SLEEP .pm = &pxa2xx_ac97_pm_ops, -#endif .of_match_table = of_match_ptr(pxa2xx_ac97_dt_ids), }, }; -- cgit v1.2.3 From 2b9cdef13648bebf79f029deb622e02099146c18 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 12 Feb 2024 14:52:58 +0200 Subject: ASoC: SOF: imx: Add devicetree support to select topologies We describe tplg_file_name and drv_name using snd_sof_of_mach array and select correct machine description based on dts compatible string. Signed-off-by: Daniel Baluta Reviewed-by: Iuliana Prodan Reviewed-by: Laurentiu Mihalcea Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240212125258.420265-1-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 16 ++++++++++++++++ sound/soc/sof/imx/imx8m.c | 10 ++++++++++ sound/soc/sof/imx/imx8ulp.c | 10 ++++++++++ 3 files changed, 36 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index d777e70250ef..07f51489d6c9 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -607,7 +607,22 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; +static struct snd_sof_of_mach sof_imx8_machs[] = { + { + .compatible = "fsl,imx8qxp-mek", + .sof_tplg_filename = "sof-imx8-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + { + .compatible = "fsl,imx8qm-mek", + .sof_tplg_filename = "sof-imx8-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8qxp_desc = { + .of_machines = sof_imx8_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { @@ -624,6 +639,7 @@ static struct sof_dev_desc sof_of_imx8qxp_desc = { }; static struct sof_dev_desc sof_of_imx8qm_desc = { + .of_machines = sof_imx8_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 1b976fa500aa..222cd1467da6 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = { SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; +static struct snd_sof_of_mach sof_imx8mp_machs[] = { + { + .compatible = "fsl,imx8mp-evk", + .sof_tplg_filename = "sof-imx8mp-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8mp_desc = { + .of_machines = sof_imx8mp_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c index 2badca75782b..7b527ffde488 100644 --- a/sound/soc/sof/imx/imx8ulp.c +++ b/sound/soc/sof/imx/imx8ulp.c @@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = { .set_power_state = imx8ulp_dsp_set_power_state, }; +static struct snd_sof_of_mach sof_imx8ulp_machs[] = { + { + .compatible = "fsl,imx8ulp-evk", + .sof_tplg_filename = "sof-imx8ulp-btsco.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + {} +}; + static struct sof_dev_desc sof_of_imx8ulp_desc = { + .of_machines = sof_imx8ulp_machs, .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), .ipc_default = SOF_IPC_TYPE_3, .default_fw_path = { -- cgit v1.2.3 From 00933c4993f132a53d31f995a011945b3835826c Mon Sep 17 00:00:00 2001 From: Yinchuan Guo Date: Mon, 12 Feb 2024 22:42:45 +0800 Subject: ASoC: codecs: fix TYPO 'reguest' to 'request' in error log This patch corrects a common misspelling of "request" as "reguest" found in error log across multiple files within sound/soc/codecs. Signed-off-by: Yinchuan Guo Link: https://msgid.link/r/20240212144247.43744-1-guoych37@mail2.sysu.edu.cn Signed-off-by: Mark Brown --- sound/soc/codecs/rt274.c | 2 +- sound/soc/codecs/rt286.c | 2 +- sound/soc/codecs/rt298.c | 2 +- sound/soc/codecs/rt5514-spi.c | 2 +- sound/soc/codecs/rt5645.c | 2 +- sound/soc/codecs/rt5651.c | 2 +- sound/soc/codecs/rt5659.c | 2 +- sound/soc/codecs/rt5663.c | 2 +- sound/soc/codecs/rt5665.c | 2 +- sound/soc/codecs/rt5668.c | 2 +- sound/soc/codecs/rt5682-i2c.c | 2 +- sound/soc/codecs/rt5682s.c | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 9a33e3776b55..6e7843484250 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1192,7 +1192,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 981155b046fd..f8994f4968c5 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1237,7 +1237,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index ad3783ade1b5..03d9839a5de3 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1284,7 +1284,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); if (ret != 0) { dev_err(&i2c->dev, - "Failed to reguest IRQ: %d\n", ret); + "Failed to request IRQ: %d\n", ret); return ret; } } diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 3ee6d85268ba..f475c8cfadae 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -279,7 +279,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component) rt5514_dsp); if (ret) dev_err(&rt5514_spi->dev, - "%s Failed to reguest IRQ: %d\n", __func__, + "%s Failed to request IRQ: %d\n", __func__, ret); else device_init_wakeup(rt5514_dsp->dev, true); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 5150d6ee3748..61624c502261 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -4197,7 +4197,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5645", rt5645); if (ret) { - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); goto err_enable; } } diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 0cee4fd1c84b..33a34bd0b405 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2261,7 +2261,7 @@ static int rt5651_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651); if (ret) { - dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + dev_warn(&i2c->dev, "Failed to request IRQ %d: %d\n", rt5651->irq, ret); rt5651->irq = -ENXIO; } diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index a061028a16d8..fb094c0fe740 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4292,7 +4292,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5659", rt5659); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); /* Enable IRQ output for GPIO1 pin any way */ regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 9550492605ac..161dcb3915f9 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3692,7 +3692,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5663", rt5663); if (ret) { - dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", + dev_err(&i2c->dev, "%s Failed to request IRQ: %d\n", __func__, ret); goto err_enable; } diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index a39de4a7df00..6f778c8f0832 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4929,7 +4929,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5665", rt5665); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 4623b3e62487..6d8e228ccb57 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2580,7 +2580,7 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5668", rt5668); if (ret) - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index fbad1ed06626..62f26ce9d476 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -266,7 +266,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } #ifdef CONFIG_COMMON_CLK diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 3322056bbb3b..12741668fdb3 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -3283,7 +3283,7 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) if (!ret) rt5682s->irq = i2c->irq; else - dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); } return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev, -- cgit v1.2.3 From 3858464de57b77db51f83e3831950cf18a6aff28 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:33 +0200 Subject: ASoC: SOF: ipc4-topology: change chain_dma handling in dai_config The chain_dma mode is currently only handled for HDaudio, but can be used for orther DAIs starting with LunarLake. Move the chain_dma handling earlier. Error detection for the chain_dma case for older platforms is handled at a different level. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index f779156fe0e6..8ac35e6df75f 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2796,13 +2796,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (!data) return 0; + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); + return 0; + } + switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: - if (pipeline->use_chain_dma) { - pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; - pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); - break; - } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; fallthrough; -- cgit v1.2.3 From ba91d0919a78d344d19b02a3899d0921b2f903d1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:34 +0200 Subject: ASoC: SOF: ops: add new 'is_chain_dma_supported' callback IPC4 introduced a 'chain-dma' mode when host and link DMA are connected by firmware without using a regular pipeline or the ability to add intermediate connections. This mode is not available on all platforms and all links, so add a platform-specific callback to help the SOF ipc4-topology core handle different hardware+firmware configurations. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 9 +++++++++ sound/soc/sof/sof-priv.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 6538d9f4fe96..6cf21e829e07 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -567,6 +567,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, sof_ops(sdev)->set_mach_params(mach, sdev); } +static inline bool +snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported) + return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type); + + return false; +} + /** * snd_sof_dsp_register_poll_timeout - Periodically poll an address * until a condition is met or a timeout occurs diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6d7897bf9607..6c163c008607 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -338,6 +338,8 @@ struct snd_sof_dsp_ops { struct snd_soc_dai_driver *drv; int num_drv; + bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */ + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ u32 hw_info; -- cgit v1.2.3 From d69f9ecbe1ecfa97b9c8ab7b6332bd73ba2ff4d8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:35 +0200 Subject: ASoC: SOF: Intel: hda: add 'is_chain_dma_supported' callback Reuse existing function to get the interface mask and expose it to the SOF core with a callback - the main user is the IPC4 topology so only HDaudio platforms provide this callback. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-common-ops.c | 1 + sound/soc/sof/intel/hda.c | 63 +++++++++++++++++++++++++++++------- sound/soc/sof/intel/hda.h | 5 +++ sound/soc/sof/sof-priv.h | 7 ++++ 4 files changed, 64 insertions(+), 12 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 26105d8f1bdc..2b385cddc385 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -83,6 +83,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { /* DAI drivers */ .drv = skl_dai, .num_drv = SOF_SKL_NUM_DAIS, + .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ .suspend = hda_dsp_suspend, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index fe4ae349dad5..0bae439feb8b 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -46,44 +46,83 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask) { const struct sof_intel_dsp_desc *chip; - u32 interface_mask[2] = { 0 }; chip = get_chip_info(sdev->pdata); switch (chip->hw_ip_version) { case SOF_INTEL_TANGIER: case SOF_INTEL_BAYTRAIL: case SOF_INTEL_BROADWELL: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP); + interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP); break; case SOF_INTEL_CAVS_1_5: case SOF_INTEL_CAVS_1_5_PLUS: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_CAVS_1_8: case SOF_INTEL_CAVS_2_0: case SOF_INTEL_CAVS_2_5: case SOF_INTEL_ACE_1_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA); break; case SOF_INTEL_ACE_2_0: - interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | - BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); - interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_DSP_ACCESS] = + BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + /* all interfaces accessible without DSP */ + interface_mask[SOF_DAI_HOST_ACCESS] = + interface_mask[SOF_DAI_DSP_ACCESS]; break; default: break; } +} + +static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + + hda_get_interfaces(sdev, interface_mask); return interface_mask[sdev->dspless_mode_selected]; } +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type) +{ + u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 }; + const struct sof_intel_dsp_desc *chip; + + if (sdev->dspless_mode_selected) + return false; + + hda_get_interfaces(sdev, interface_mask); + + if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type))) + return false; + + if (dai_type == SOF_DAI_INTEL_HDA) + return true; + + switch (dai_type) { + case SOF_DAI_INTEL_SSP: + case SOF_DAI_INTEL_DMIC: + case SOF_DAI_INTEL_ALH: + chip = get_chip_info(sdev->pdata); + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return false; + return true; + default: + return false; + } +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) /* diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 1592e27bc14d..b36eb7c78913 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -573,6 +573,11 @@ struct sof_intel_hda_stream { #define SOF_STREAM_SD_OFFSET_CRST 0x1 +/* + * DAI support + */ +bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type); + /* * DSP Core services. */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6c163c008607..5e5c5a36c3c9 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -157,6 +157,13 @@ struct sof_firmware { u32 payload_offset; }; +enum sof_dai_access { + SOF_DAI_DSP_ACCESS, /* access from DSP only */ + SOF_DAI_HOST_ACCESS, /* access from host only */ + + SOF_DAI_ACCESS_NUM +}; + /* * SOF DSP HW abstraction operations. * Used to abstract DSP HW architecture and any IO busses between host CPU -- cgit v1.2.3 From a5b7767723e739c700f5c56841790a85bd7f13ae Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:36 +0200 Subject: ASoC: SOF: Intel: hda-dai-ops: enable chain_dma for ALH Use the existing callbacks and mix/match of HDaudio and SoundWire support. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai-ops.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 55ce75db23e5..f58539d2f937 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -522,6 +522,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = { + .get_hext_stream = hda_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .trigger = hda_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -620,6 +631,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg } case SOF_IPC_TYPE_4: { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_copier *ipc4_copier = sdai->private; const struct sof_intel_dsp_desc *chip; @@ -627,15 +640,10 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: - { - struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->use_chain_dma) return &hda_ipc4_chain_dma_ops; return &hda_ipc4_dma_ops; - } case SOF_DAI_INTEL_SSP: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; @@ -647,6 +655,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg case SOF_DAI_INTEL_ALH: if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) return NULL; + if (pipeline->use_chain_dma) + return &sdw_ipc4_chain_dma_ops; return &sdw_ipc4_dma_ops; default: -- cgit v1.2.3 From 426476344f01096c7dae6c5413cc8a8d9bbdea29 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:37 +0200 Subject: ASoC: SOF: ipc4: store number of playback/capture streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CHAIN_DMA IPC needs the number of playback streams as a start offset for the dma_id of a capture stream. This offset can be retrieved on Intel platforms from the GCAP information, and stored in the sof_ipc4_fw_data structure. One could argue that the fields added are not really dependent on any firmware definitions but rather on hardware capabilities, but they are required for the IPC CHAIN_DMA definitions so adding them in ipc4_fw_data isn't completely silly. The CHAIN_DMA IPC is currently only functional on Intel HDaudio DMAs, and gated by the snd_sof_is_chain_dma_supported() helper. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 9 +++++++++ sound/soc/sof/ipc4-priv.h | 4 ++++ 2 files changed, 13 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index f2ebadbbcc10..b387b1a69d7e 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -21,6 +21,7 @@ #include #include "../ops.h" #include "../sof-audio.h" +#include "../ipc4-priv.h" #include "hda.h" #define HDA_LTRP_GB_VALUE_US 95 @@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) /* store total stream count (playback + capture) from GCAP */ sof_hda->stream_max = num_total; + /* store stream count from GCAP required for CHAIN_DMA */ + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + + ipc4_data->num_playback_streams = num_playback; + ipc4_data->num_capture_streams = num_capture; + } + return 0; } diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 1d39836d5efa..f3b908b093f9 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -66,6 +66,8 @@ struct sof_ipc4_fw_library { * @nhlt: NHLT table either from the BIOS or the topology manifest * @mtrace_type: mtrace type supported on the booted platform * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply + * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset + * @num_capture_streams: max number of capture DMAs * @max_num_pipelines: max number of pipelines * @max_libs_count: Maximum number of libraries support by the FW including the * base firmware @@ -79,6 +81,8 @@ struct sof_ipc4_fw_data { void *nhlt; enum sof_ipc4_mtrace_type mtrace_type; u32 mtrace_log_bytes; + int num_playback_streams; + int num_capture_streams; int max_num_pipelines; u32 max_libs_count; bool fw_context_save; -- cgit v1.2.3 From 8722d245a73ff32491bff390136367ec223e2906 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:38 +0200 Subject: ASoC: SOF: ipc4-pcm: fix dma_id for CHAIN_DMA capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code uses (stream_tag - 1) for the host and link dma id. This is correct for playback, but for capture this results in an invalid dma_type being used. The firmware assumes that the dma_id for capture is always larger than DAI_NUM_HDA_OUT This patch adds the offset for num_playback_streams, filled on Intel platforms with the value extracted from the hardware capabilities. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 85d3f390e4b2..035c44ce6c9d 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -231,9 +231,11 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, */ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, + int direction, struct snd_sof_pcm_stream_pipeline_list *pipeline_list, int state, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; bool allocate, enable, set_fifo_size; struct sof_ipc4_msg msg = {{ 0 }}; int i; @@ -294,6 +296,20 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, msg.extension |= pipeline->msg.extension; } + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + /* + * For ChainDMA the DMA ids are unique with the following mapping: + * playback: 0 - (num_playback_streams - 1) + * capture: num_playback_streams - (num_playback_streams + + * num_capture_streams - 1) + * + * Add the num_playback_streams offset to the DMA ids stored in + * msg.primary in case capture + */ + msg.primary += SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(ipc4_data->num_playback_streams); + msg.primary += SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(ipc4_data->num_playback_streams); + } + if (allocate) msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; @@ -340,7 +356,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * trigger function that handles the rest for the substream. */ if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); + return sof_ipc4_chain_dma_trigger(sdev, substream->stream, + pipeline_list, state, cmd); /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, -- cgit v1.2.3 From df82dbb5fb28a762113fc6d98985d36ef7785e32 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:39 +0200 Subject: ASoC: SOF: ipc4-topology: allow chain_dma for all supported DAIs Now that we have a 'is_chain_dma_supported' callback we can use it to double-check possible disconnects between a topology file enabling chain-dma for a DAI and the hardware/firmware capabilities. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 8ac35e6df75f..98e2f83b1c09 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -509,6 +509,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_widget *pipe_widget; @@ -552,10 +553,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; - if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { - dev_err(scomp->dev, - "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", - ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); + + if (pipeline->use_chain_dma && + !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) { + dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n", + ipc4_copier->dai_type); ret = -ENODEV; goto free_available_fmt; } -- cgit v1.2.3 From daa09d0615ce9c781777802874cffa4380f883c3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:40 +0200 Subject: ASoC: SOF: Intel: hda-dai: remove dspless special case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing code forces a parameter to be NULL but that parameter is not used yet. Remove the special case in preparation for additional changes. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index f4cbc0ad5de3..4bffd9ea21a9 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -83,12 +83,8 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai sdev = widget_to_sdev(w); - /* - * The swidget parameter of hda_select_dai_widget_ops() is ignored in - * case of DSPless mode - */ if (sdev->dspless_mode_selected) - return hda_select_dai_widget_ops(sdev, NULL); + return hda_select_dai_widget_ops(sdev, swidget); sdai = swidget->private; -- cgit v1.2.3 From 743eb6c68d3534e01e73d316ddcaa7334c0e29d3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:41 +0200 Subject: ASoC: SOF: topology: dynamically allocate and store DAI widget->private MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For dspless mode, we need to allocate and store an 'sdai' structure. The existing code allocate the data on the stack and does not set the widget->private pointer. This minor change should not have any impact on existing DAIs, even when the DSP is used. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 8 +++----- sound/soc/sof/topology.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9163975c9c3f..e693dcb475e4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_pipeline *spipe = swidget->spipe; - struct snd_sof_widget *pipe_widget; int err = 0; int ret; @@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, if (--swidget->use_count) return 0; - pipe_widget = swidget->spipe->pipe_widget; - /* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); @@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, * free the scheduler widget (same as pipe_widget) associated with the current swidget. * skip for static pipelines */ - if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free_unlocked(sdev, pipe_widget); + if (swidget->spipe && swidget->dynamic_pipeline_widget && + swidget->id != snd_soc_dapm_scheduler) { + ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); if (ret < 0 && !err) err = ret; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 25fb0d1443b6..915c2e88e32b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2356,23 +2356,29 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, if (WIDGET_IS_DAI(w->id)) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget; - struct snd_sof_dai dai; + struct snd_sof_dai *sdai; int ret; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); if (!swidget) return -ENOMEM; - memset(&dai, 0, sizeof(dai)); + sdai = kzalloc(sizeof(*sdai), GFP_KERNEL); + if (!sdai) { + kfree(swidget); + return -ENOMEM; + } - ret = sof_connect_dai_widget(scomp, w, tw, &dai); + ret = sof_connect_dai_widget(scomp, w, tw, sdai); if (ret) { kfree(swidget); + kfree(sdai); return ret; } swidget->scomp = scomp; swidget->widget = w; + swidget->private = sdai; mutex_init(&swidget->setup_mutex); w->dobj.private = swidget; list_add(&swidget->list, &sdev->widget_list); @@ -2396,6 +2402,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp, /* remove and free swidget object */ list_del(&swidget->list); + kfree(swidget->private); kfree(swidget); } -- cgit v1.2.3 From 67bde2e8c0e4702911dd614d80127e098521a83c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:42 +0200 Subject: ASoC: SOF: Intel: start SoundWire links earlier for LNL+ devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SoundWire integration is different from previous platforms, with no dependencies on the DSP enablement. We can start the SoundWire links in the probe instead of waiting for the post_fw_run stage. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 5 +++++ sound/soc/sof/intel/hda.c | 17 +++++++++++++++++ sound/soc/sof/intel/lnl.c | 15 ++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 2445ae7f6b2e..31ffa1a8f2ac 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -748,6 +748,7 @@ skip_dsp: static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) { + const struct sof_intel_dsp_desc *chip; int ret; /* display codec must be powered before link reset */ @@ -780,6 +781,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume) hda_dsp_ctrl_ppcap_int_enable(sdev, true); } + chip = get_chip_info(sdev->pdata); + if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) + hda_sdw_int_enable(sdev, true); + cleanup: /* display codec can powered off after controller init */ hda_codec_i915_display_power(sdev, false); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 0bae439feb8b..7fe72b065451 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1231,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip; int ret = 0; hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec", @@ -1344,12 +1345,28 @@ skip_dsp_setup: INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work); } + chip = get_chip_info(sdev->pdata); + if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "could not startup SoundWire links\n"); + goto disable_pp_cap; + } + + hda_sdw_int_enable(sdev, true); + } + init_waitqueue_head(&hdev->waitq); hdev->nhlt = intel_nhlt_init(sdev->dev); return 0; +disable_pp_cap: + if (!sdev->dspless_mode_selected) { + hda_dsp_ctrl_ppcap_int_enable(sdev, false); + hda_dsp_ctrl_ppcap_enable(sdev, false); + } free_ipc_irq: free_irq(sdev->ipc_irq, sdev); free_irq_vector: diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index 30712ea05a7a..b2ade2741dce 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -77,6 +77,19 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); } +static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev) +{ + if (sdev->first_boot) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + + /* Check if IMR boot is usable */ + if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) + hda->imrboot_supported = true; + } + + return 0; +} + int sof_lnl_ops_init(struct snd_sof_dev *sdev) { struct sof_ipc4_fw_data *ipc4_data; @@ -106,7 +119,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) /* pre/post fw run */ sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; - sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run; + sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run; /* parse platform specific extended manifest */ sof_lnl_ops.parse_platform_ext_manifest = NULL; -- cgit v1.2.3 From f9618ff105a0f6f5a6beed3edc557ea6a7d26df6 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 13 Feb 2024 12:12:43 +0200 Subject: ASoC: SOF: topology: Parse DAI type token for dspless mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting with LunarLake, the dspless mode can handle SoundWire/ALH, DMIC and SSPs, so we need to identify the dai type from topology. Signed-off-by: Ranjani Sridharan Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 1 + sound/soc/sof/sof-audio.h | 1 + sound/soc/sof/topology.c | 12 ++++++++++++ 3 files changed, 14 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 98e2f83b1c09..43d4abd79f44 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -549,6 +549,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name, node_type, ipc4_copier->dai_type, ipc4_copier->dai_index); + dai->type = ipc4_copier->dai_type; ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); pipe_widget = swidget->spipe->pipe_widget; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index f98242a404db..9ea2ac5adac7 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -514,6 +514,7 @@ struct snd_sof_route { struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; + u32 type; int number_configs; int current_config; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 915c2e88e32b..bcdb499c96a0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2354,7 +2354,10 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { 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; @@ -2369,6 +2372,15 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, return -ENOMEM; } + ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name); + kfree(swidget); + kfree(sdai); + return ret; + } + ret = sof_connect_dai_widget(scomp, w, tw, sdai); if (ret) { kfree(swidget); -- cgit v1.2.3 From 797b92591a236ac370257c1e742f6fd394993db5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:44 +0200 Subject: ASoC: SOF: Intel: hda-dai-ops: use dai_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have the dai_type we can remove any dependencies on copiers. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai-ops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f58539d2f937..5a5ef93858be 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -633,12 +633,11 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - struct sof_ipc4_copier *ipc4_copier = sdai->private; const struct sof_intel_dsp_desc *chip; chip = get_chip_info(sdev->pdata); - switch (ipc4_copier->dai_type) { + switch (sdai->type) { case SOF_DAI_INTEL_HDA: if (pipeline->use_chain_dma) return &hda_ipc4_chain_dma_ops; -- cgit v1.2.3 From 0c3d57365a03ec920cc90614527ac11ad5a6f323 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:45 +0200 Subject: ASoC: SOF: Intel: hda-dai-ops: add SoundWire dspless mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mode is only supported starting with LunarLake (ACE_2_0). DMIC and SSP remain supported with the DSP only for now, since they need a DAI configuration that is provided to firmware. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai-ops.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 5a5ef93858be..c50ca9e72d37 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -607,6 +607,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = { + .get_hext_stream = hda_dspless_get_hext_stream, + .setup_hext_stream = hda_dspless_setup_hext_stream, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + #endif const struct hda_dai_widget_dma_ops * @@ -614,12 +621,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct snd_sof_dai *sdai; + const struct sof_intel_dsp_desc *chip; - if (sdev->dspless_mode_selected) - return &hda_dspless_dma_ops; - + chip = get_chip_info(sdev->pdata); sdai = swidget->private; + if (sdev->dspless_mode_selected) { + switch (sdai->type) { + case SOF_DAI_INTEL_HDA: + return &hda_dspless_dma_ops; + case SOF_DAI_INTEL_ALH: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &sdw_dspless_dma_ops; + default: + return NULL; + } + } + switch (sdev->pdata->ipc_type) { case SOF_IPC_TYPE_3: { @@ -633,9 +652,6 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - const struct sof_intel_dsp_desc *chip; - - chip = get_chip_info(sdev->pdata); switch (sdai->type) { case SOF_DAI_INTEL_HDA: -- cgit v1.2.3 From 0afce89ff88a85b09201cd23ec272527faf6a480 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Feb 2024 12:12:46 +0200 Subject: ASoC: SOF: Intel: lnl: Do not use LNL specific wrappers in DSPless mode When DSPless mode is selected the DMIC/SSP offload status should not be changed since the DSP is not in use. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://msgid.link/r/20240213101247.28887-15-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/lnl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index b2ade2741dce..7ae017a00184 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -98,7 +98,8 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); /* probe */ - sof_lnl_ops.probe = lnl_hda_dsp_probe; + if (!sdev->dspless_mode_selected) + sof_lnl_ops.probe = lnl_hda_dsp_probe; /* shutdown */ sof_lnl_ops.shutdown = hda_dsp_shutdown; @@ -128,8 +129,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) /* TODO: add core_get and core_put */ /* PM */ - sof_lnl_ops.resume = lnl_hda_dsp_resume; - sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + if (!sdev->dspless_mode_selected) { + sof_lnl_ops.resume = lnl_hda_dsp_resume; + sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + } sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; -- cgit v1.2.3 From 2065610b5ddd5b58eed1dc3b3c3db27a26ebd4b6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 13 Feb 2024 12:12:47 +0200 Subject: ASoC: SOF: Intel: hda-dai: add support for dspless mode beyond HDAudio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For SoundWire/ALH, we need to have a dai configured, but we don't want to send a DMA_TLV to firmware. Add additional code branches. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213101247.28887-16-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 4bffd9ea21a9..c1682bcdb5a6 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -83,6 +83,11 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai sdev = widget_to_sdev(w); + if (!swidget) { + dev_err(sdev->dev, "%s: swidget is NULL\n", __func__); + return NULL; + } + if (sdev->dspless_mode_selected) return hda_select_dai_widget_ops(sdev, swidget); @@ -364,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, return ret; } - /* get stream_id */ sdev = widget_to_sdev(w); + if (sdev->dspless_mode_selected) + goto skip_tlv; + + /* get stream_id */ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) { @@ -398,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ dma_config->dma_priv_config_size = 0; +skip_tlv: return 0; } -- cgit v1.2.3 From b30289e7fa927f921bfb4d0d04727461706ae822 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 Feb 2024 13:47:29 +0200 Subject: ASoC: SOF: Fix runtime pm usage counter balance after fw exception If the retain context is enabled we will unconditionally increment the device's pm use count on each exception and when the drivers are unloaded we do not correct this (as we don't know how many times we 'prevented d3 entry'). Introduce a flag to make sure that we do not increment the use count more than once and on module unload decrement the use count if needed to balance it. Signed-off-by: Peter Ujfalusi Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240213114729.7055-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 10 ++++++++++ sound/soc/sof/debug.c | 8 +++++--- sound/soc/sof/sof-priv.h | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 425b023b03b4..9b00ede2a486 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -679,6 +679,16 @@ int snd_sof_device_remove(struct device *dev) */ snd_sof_machine_unregister(sdev, pdata); + /* + * Balance the runtime pm usage count in case we are faced with an + * exception and we forcably prevented D3 power state to preserve + * context + */ + if (sdev->d3_prevented) { + sdev->d3_prevented = false; + pm_runtime_put_noidle(sdev->dev); + } + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { sof_fw_trace_free(sdev); ret = snd_sof_dsp_power_down_notify(sdev); diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index d547318e0d32..7c8aafca8fde 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -433,13 +433,15 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg) { - if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || - sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { + if ((IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || + sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) && !sdev->d3_prevented) { /* should we prevent DSP entering D3 ? */ if (!sdev->ipc_dump_printed) dev_info(sdev->dev, "Attempting to prevent DSP from entering D3 state to preserve context\n"); - pm_runtime_get_if_in_use(sdev->dev); + + if (pm_runtime_get_if_in_use(sdev->dev) == 1) + sdev->d3_prevented = true; } /* dump vital information to the logs */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 6d7897bf9607..5755c997a9de 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -595,6 +595,7 @@ struct snd_sof_dev { struct list_head dfsentry_list; bool dbg_dump_printed; bool ipc_dump_printed; + bool d3_prevented; /* runtime pm use count incremented to prevent context lost */ /* firmware loader */ struct sof_ipc_fw_ready fw_ready; -- cgit v1.2.3 From e49676a5fc83e5d396f45ce4b90ca9c44736c69a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 13 Feb 2024 14:30:07 +0200 Subject: ASoC: SOF: ipc4-topology: set config_length based on device_count Set ipc4_copier->data.gtw_cfg.config_length dynamically based on blob->alh_cfg.device_count to align with the other OS. Signed-off-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240213123007.29956-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index f779156fe0e6..1dc935d737dd 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -598,7 +598,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) } ipc4_copier->copier_config = (uint32_t *)blob; - ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2; + /* set data.gtw_cfg.config_length based on device_count */ + ipc4_copier->data.gtw_cfg.config_length = (sizeof(blob->gw_attr) + + sizeof(blob->alh_cfg.device_count) + + sizeof(*blob->alh_cfg.mapping) * + blob->alh_cfg.device_count) >> 2; break; } case SOF_DAI_INTEL_SSP: -- cgit v1.2.3 From a6eb64e7e32c7a6a502a19c20e3f04818091c2dc Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sat, 3 Feb 2024 21:43:05 +0100 Subject: ASoC: codecs: va-macro: add npl clk New versions of VA Macro has soundwire integrated, so handle the soundwire npl clock correctly in the codec driver. Introduce has_npl_clk and handle the sm8550 case separately because it has soundwire integrated but doesn't have an npl clock. Signed-off-by: Srinivas Kandagatla Signed-off-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Link: https://msgid.link/r/20240203-topic-sm8x50-upstream-va-macro-npl-v2-1-f2db82ae3359@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-va-macro.c | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index b71ef03c4aef..6eceeff10bf6 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -201,10 +201,12 @@ struct va_macro { unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; u16 dmic_clk_div; bool has_swr_master; + bool has_npl_clk; int dec_mode[VA_MACRO_NUM_DECIMATORS]; struct regmap *regmap; struct clk *mclk; + struct clk *npl; struct clk *macro; struct clk *dcodec; struct clk *fsgen; @@ -225,14 +227,22 @@ struct va_macro { struct va_macro_data { bool has_swr_master; + bool has_npl_clk; }; static const struct va_macro_data sm8250_va_data = { .has_swr_master = false, + .has_npl_clk = false, }; static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, + .has_npl_clk = true, +}; + +static const struct va_macro_data sm8550_va_data = { + .has_swr_master = true, + .has_npl_clk = false, }; static bool va_is_volatile_register(struct device *dev, unsigned int reg) @@ -1332,6 +1342,12 @@ static int fsgen_gate_enable(struct clk_hw *hw) struct regmap *regmap = va->regmap; int ret; + if (va->has_swr_master) { + ret = clk_prepare_enable(va->mclk); + if (ret) + return ret; + } + ret = va_macro_mclk_enable(va, true); if (va->has_swr_master) regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, @@ -1350,6 +1366,8 @@ static void fsgen_gate_disable(struct clk_hw *hw) CDC_VA_SWR_CLK_EN_MASK, 0x0); va_macro_mclk_enable(va, false); + if (va->has_swr_master) + clk_disable_unprepare(va->mclk); } static int fsgen_gate_is_enabled(struct clk_hw *hw) @@ -1378,6 +1396,9 @@ static int va_macro_register_fsgen_output(struct va_macro *va) struct clk_init_data init; int ret; + if (va->has_npl_clk) + parent = va->npl; + parent_clk_name = __clk_get_name(parent); of_property_read_string(np, "clock-output-names", &clk_name); @@ -1500,10 +1521,21 @@ static int va_macro_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); va->has_swr_master = data->has_swr_master; + va->has_npl_clk = data->has_npl_clk; /* mclk rate */ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + if (va->has_npl_clk) { + va->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(va->npl)) { + ret = PTR_ERR(va->npl); + goto err; + } + + clk_set_rate(va->npl, 2 * VA_MACRO_MCLK_FREQ); + } + ret = clk_prepare_enable(va->macro); if (ret) goto err; @@ -1516,6 +1548,12 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_mclk; + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) + goto err_npl; + } + if (va->has_swr_master) { /* Set default CLK div to 1 */ regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, @@ -1564,6 +1602,9 @@ static int va_macro_probe(struct platform_device *pdev) return 0; err_clkout: + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); +err_npl: clk_disable_unprepare(va->mclk); err_mclk: clk_disable_unprepare(va->dcodec); @@ -1579,6 +1620,9 @@ static void va_macro_remove(struct platform_device *pdev) { struct va_macro *va = dev_get_drvdata(&pdev->dev); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); clk_disable_unprepare(va->dcodec); clk_disable_unprepare(va->macro); @@ -1593,6 +1637,9 @@ static int __maybe_unused va_macro_runtime_suspend(struct device *dev) regcache_cache_only(va->regmap, true); regcache_mark_dirty(va->regmap); + if (va->has_npl_clk) + clk_disable_unprepare(va->npl); + clk_disable_unprepare(va->mclk); return 0; @@ -1609,6 +1656,15 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) return ret; } + if (va->has_npl_clk) { + ret = clk_prepare_enable(va->npl); + if (ret) { + clk_disable_unprepare(va->mclk); + dev_err(va->dev, "unable to prepare npl\n"); + return ret; + } + } + regcache_cache_only(va->regmap, false); regcache_sync(va->regmap); @@ -1624,6 +1680,7 @@ static const struct of_device_id va_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, + { .compatible = "qcom,sm8550-lpass-va-macro", .data = &sm8550_va_data }, { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data }, {} }; -- cgit v1.2.3 From 58cef044e6ec88eef6f10565df8257138e2085ec Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Feb 2024 16:41:32 +0100 Subject: ASoC: codecs: tx-macro: Drop unimplemented DMIC clock divider Downstream driver configures DMIC clock rate through the divider register but only parts of this code ended up in the upstream driver: we always write the same value 0, so DIV2. Same default value is used also for the AMIC rate control. Let's make it obvious and drop unneeded parts of the code. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240202154134.66967-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 124c2e144f33..cdceccf64ac8 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -38,6 +38,8 @@ #define CDC_TX_TOP_CSR_I2S_RESET (0x00AC) #define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4) #define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0) +/* Default divider for AMIC and DMIC clock: DIV2 */ +#define CDC_TX_SWR_MIC_CLK_DEFAULT 0 #define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1) #define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4) #define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8) @@ -270,7 +272,6 @@ struct tx_macro { struct clk_hw hw; bool dec_active[NUM_DECIMATORS]; int tx_mclk_users; - u16 dmic_clk_div; bool bcs_enable; int dec_mode[NUM_DECIMATORS]; struct lpass_macro *pds; @@ -743,7 +744,6 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, unsigned int val, dmic; u16 mic_sel_reg; u16 dmic_clk_reg; - struct tx_macro *tx = snd_soc_component_get_drvdata(component); val = ucontrol->value.enumerated.item[0]; if (val >= e->items) @@ -793,7 +793,7 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); snd_soc_component_write_field(component, dmic_clk_reg, CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); + CDC_TX_SWR_MIC_CLK_DEFAULT); } } @@ -882,7 +882,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_write_field(component, dmic_clk_reg, CDC_TX_SWR_DMIC_CLK_SEL_MASK, - tx->dmic_clk_div); + CDC_TX_SWR_MIC_CLK_DEFAULT); } } snd_soc_component_write_field(component, dec_cfg_reg, -- cgit v1.2.3 From b396071681ca65e66f2a8fce240cde26a6db5931 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Feb 2024 16:41:33 +0100 Subject: ASoC: codecs: tx-macro: Mark AMIC control registers as volatile Just like DMIC, the AMIC control registers are volatile. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240202154134.66967-3-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index cdceccf64ac8..2d4f6c04332b 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -432,6 +432,8 @@ static bool tx_is_volatile_register(struct device *dev, unsigned int reg) case CDC_TX_TOP_CSR_SWR_DMIC1_CTL: case CDC_TX_TOP_CSR_SWR_DMIC2_CTL: case CDC_TX_TOP_CSR_SWR_DMIC3_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: return true; } return false; -- cgit v1.2.3 From fd236653ab60bf64fde341ed9c940c04a542483a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 2 Feb 2024 16:41:34 +0100 Subject: ASoC: codecs: tx-macro: Simplify setting AMIC control When updating all bits in AMIC control registers (mask 0xff), use more obvious snd_soc_component_write(). Replace also hard-coded value 0x00 with a define. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240202154134.66967-4-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-tx-macro.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 2d4f6c04332b..7e51212d4503 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1850,8 +1850,10 @@ static int tx_macro_component_probe(struct snd_soc_component *comp) snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F, 0x0A); /* Enable swr mic0 and mic1 clock */ - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00); - snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); + snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, + CDC_TX_SWR_MIC_CLK_DEFAULT); return 0; } -- cgit v1.2.3 From 98ac85a00f31d2e9d5452b825a9ed0153d934043 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 13 Feb 2024 22:58:03 +0100 Subject: ASoC: meson: aiu: fix function pointer type mismatch clang-16 warns about casting functions to incompatible types, as is done here to call clk_disable_unprepare: sound/soc/meson/aiu.c:243:12: error: cast from 'void (*)(struct clk *)' to 'void (*)(void *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 243 | (void(*)(void *))clk_disable_unprepare, The pattern of getting, enabling and setting a disable callback for a clock can be replaced with devm_clk_get_enabled(), which also fixes this warning. Fixes: 6ae9ca9ce986 ("ASoC: meson: aiu: add i2s and spdif support") Reported-by: Arnd Bergmann Signed-off-by: Jerome Brunet Reviewed-by: Justin Stitt Link: https://msgid.link/r/20240213215807.3326688-2-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu.c | 19 ++++--------------- sound/soc/meson/aiu.h | 1 - 2 files changed, 4 insertions(+), 16 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index 7109b81cc3d0..5d1419ed7a62 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -212,11 +212,12 @@ static const char * const aiu_spdif_ids[] = { static int aiu_clk_get(struct device *dev) { struct aiu *aiu = dev_get_drvdata(dev); + struct clk *pclk; int ret; - aiu->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(aiu->pclk)) - return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n"); + pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(dev, PTR_ERR(pclk), "Can't get the aiu pclk\n"); aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); if (IS_ERR(aiu->spdif_mclk)) @@ -233,18 +234,6 @@ static int aiu_clk_get(struct device *dev) if (ret) return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); - ret = clk_prepare_enable(aiu->pclk); - if (ret) { - dev_err(dev, "peripheral clock enable failed\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - aiu->pclk); - if (ret) - dev_err(dev, "failed to add reset action on pclk"); - return ret; } diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h index 393b6c2307e4..0f94c8bf6081 100644 --- a/sound/soc/meson/aiu.h +++ b/sound/soc/meson/aiu.h @@ -33,7 +33,6 @@ struct aiu_platform_data { }; struct aiu { - struct clk *pclk; struct clk *spdif_mclk; struct aiu_interface i2s; struct aiu_interface spdif; -- cgit v1.2.3 From 5ad992c71b6a8e8a547954addc7af9fbde6ca10a Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 13 Feb 2024 22:58:04 +0100 Subject: ASoC: meson: t9015: fix function pointer type mismatch clang-16 warns about casting functions to incompatible types, as is done here to call clk_disable_unprepare: sound/soc/meson/t9015.c:274:4: error: cast from 'void (*)(struct clk *)' to 'void (*)(void *)' converts to incompatible function type [-Werror,-Wcast-function-type-strict] 274 | (void(*)(void *))clk_disable_unprepare, The pattern of getting, enabling and setting a disable callback for a clock can be replaced with devm_clk_get_enabled(), which also fixes this warning. Fixes: 33901f5b9b16 ("ASoC: meson: add t9015 internal DAC driver") Reported-by: Arnd Bergmann Signed-off-by: Jerome Brunet Reviewed-by: Justin Stitt Link: https://msgid.link/r/20240213215807.3326688-3-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/t9015.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 9c6b4dac6893..571f65788c59 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -48,7 +48,6 @@ #define POWER_CFG 0x10 struct t9015 { - struct clk *pclk; struct regulator *avdd; }; @@ -249,6 +248,7 @@ static int t9015_probe(struct platform_device *pdev) struct t9015 *priv; void __iomem *regs; struct regmap *regmap; + struct clk *pclk; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -256,26 +256,14 @@ static int t9015_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, priv); - priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) - return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n"); + pclk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(pclk)) + return dev_err_probe(dev, PTR_ERR(pclk), "failed to get core clock\n"); priv->avdd = devm_regulator_get(dev, "AVDD"); if (IS_ERR(priv->avdd)) return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); - ret = clk_prepare_enable(priv->pclk); - if (ret) { - dev_err(dev, "core clock enable failed\n"); - return ret; - } - - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - priv->pclk); - if (ret) - return ret; - ret = device_reset(dev); if (ret) { dev_err(dev, "reset failed\n"); -- cgit v1.2.3 From 98f681b0f84cfc3a1d83287b77697679e0398306 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Feb 2024 16:02:16 +0300 Subject: ASoC: SOF: Add some bounds checking to firmware data Smatch complains about "head->full_size - head->header_size" can underflow. To some extent, we're always going to have to trust the firmware a bit. However, it's easy enough to add a check for negatives, and let's add a upper bounds check as well. Fixes: d2458baa799f ("ASoC: SOF: ipc3-loader: Implement firmware parsing and loading") Signed-off-by: Dan Carpenter Link: https://msgid.link/r/5593d147-058c-4de3-a6f5-540ecb96f6f8@moroto.mountain Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-loader.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c index 28218766d211..6e3ef0672110 100644 --- a/sound/soc/sof/ipc3-loader.c +++ b/sound/soc/sof/ipc3-loader.c @@ -148,6 +148,8 @@ static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) head = (struct sof_ext_man_header *)fw->data; remaining = head->full_size - head->header_size; + if (remaining < 0 || remaining > sdev->basefw.fw->size) + return -EINVAL; ext_man_size = ipc3_fw_ext_man_size(sdev, fw); /* Assert firmware starts with extended manifest */ -- cgit v1.2.3 From 74e0259495cfab4f92c64ddcbbfe454e5c2f962a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 15 Feb 2024 22:28:54 +0900 Subject: ASoC: codecs: remove redundant 'tristate' in sound/soc/codecs/Kconfig The type 'tristate' is already specified three lines above. Signed-off-by: Masahiro Yamada Link: https://msgid.link/r/20240215132854.1907630-1-masahiroy@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 58ee431edfd8..027d9da85251 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -2335,7 +2335,6 @@ config SND_SOC_WSA881X tristate "WSA881X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8810/WSA8815 Class-D Smart Speaker Amplifier. @@ -2344,7 +2343,6 @@ config SND_SOC_WSA883X tristate "WSA883X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. @@ -2353,7 +2351,6 @@ config SND_SOC_WSA884X tristate "WSA884X Codec" depends on SOUNDWIRE select REGMAP_SOUNDWIRE - tristate help This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D Smart Speaker Amplifier. -- cgit v1.2.3 From cf88ab486ab7f000e612e08c517bcd490c7c6289 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Feb 2024 15:54:48 +0100 Subject: ASoC: Constify pointer to of_phandle_args Constify pointer to of_phandle_args in few function arguments, for code safety and self-documenting code. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240216145448.224185-1-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++-- sound/soc/soc-core.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6defc5547ff9..39613b406b1d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1401,8 +1401,8 @@ void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, struct snd_soc_dai_link_component *cpus); struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, - struct of_phandle_args *args); -struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args); + const struct of_phandle_args *args); +struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args); struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, bool legacy_dai_naming); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 516350533e73..b11b2ca5d939 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -238,8 +238,8 @@ static inline void snd_soc_debugfs_exit(void) { } #endif -static int snd_soc_is_match_dai_args(struct of_phandle_args *args1, - struct of_phandle_args *args2) +static int snd_soc_is_match_dai_args(const struct of_phandle_args *args1, + const struct of_phandle_args *args2) { if (!args1 || !args2) return 0; @@ -831,7 +831,8 @@ static struct device_node return of_node; } -struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args) +struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, + const struct of_phandle_args *args) { struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL); @@ -3597,7 +3598,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); -struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args) +struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args) { struct snd_soc_dai *dai; struct snd_soc_component *component; -- cgit v1.2.3 From 0386d765f27a1fd3ed2ed6388a07e26d9659936d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 14 Feb 2024 16:10:01 +0530 Subject: ASoC: amd: ps: refactor acp device configuration read logic Refactor acp device configuration read logic and use common function to scan SoundWire devices. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240214104014.1144668-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 17 +++++ sound/soc/amd/ps/acp63.h | 11 +++ sound/soc/amd/ps/pci-ps.c | 176 +++++++++++++--------------------------------- 3 files changed, 78 insertions(+), 126 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 273688c05317..fa74635cee08 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -132,9 +132,26 @@ config SND_SOC_AMD_RPL_ACP6x Say m if you have such a device. If unsure select "N". +config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + tristate + select SOUNDWIRE_AMD if SND_SOC_AMD_SOUNDWIRE != n + select SND_AMD_SOUNDWIRE_ACPI if ACPI + +config SND_SOC_AMD_SOUNDWIRE + tristate "Support for SoundWire based AMD platforms" + default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE + depends on ACPI && SOUNDWIRE + depends on !(SOUNDWIRE=m && SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE=y) + help + This adds support for SoundWire for AMD platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + config SND_SOC_AMD_PS tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support" select SND_AMD_ACP_CONFIG + select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE depends on X86 && PCI && ACPI help This option enables Audio Coprocessor i.e ACP v6.3 support on diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 8b853b8d0219..123b9ade69d4 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -5,6 +5,7 @@ * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved. */ +#include #include #define ACP_DEVICE_ID 0x15E2 @@ -263,6 +264,11 @@ struct sdw_dma_ring_buf_reg { * @sdw0_dev_index: SoundWire Manager-0 platform device index * @sdw1_dev_index: SoundWire Manager-1 platform device index * @sdw_dma_dev_index: SoundWire DMA controller platform device index + * @info: SoundWire AMD information found in ACPI tables + * @is_sdw_dev: flag set to true when any SoundWire manager instances are available + * @is_pdm_dev: flag set to true when ACP PDM controller exists + * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS + * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance * @acp_reset: flag set to true when bus reset is applied across all @@ -282,6 +288,11 @@ struct acp63_dev_data { u16 sdw0_dev_index; u16 sdw1_dev_index; u16 sdw_dma_dev_index; + struct sdw_amd_acpi_info info; + bool is_sdw_dev; + bool is_pdm_dev; + bool is_pdm_config; + bool is_sdw_config; u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; bool acp_reset; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 5927eef04170..c97e418a88ce 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -237,122 +237,51 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) return IRQ_NONE; } -static int sdw_amd_scan_controller(struct device *dev) +#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE) +static int acp_scan_sdw_devices(struct device *dev, u64 addr) { + struct acpi_device *sdw_dev; struct acp63_dev_data *acp_data; - struct fwnode_handle *link; - char name[32]; - u32 sdw_manager_bitmap; - u8 count = 0; - u32 acp_sdw_power_mode = 0; - int index; - int ret; acp_data = dev_get_drvdata(dev); - /* - * Current implementation is based on MIPI DisCo 2.0 spec. - * Found controller, find links supported. - */ - ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list", - &sdw_manager_bitmap, 1); - - if (ret) { - dev_dbg(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret); - return -EINVAL; - } - count = hweight32(sdw_manager_bitmap); - /* Check count is within bounds */ - if (count > AMD_SDW_MAX_MANAGERS) { - dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS); - return -EINVAL; - } + if (!addr) + return -ENODEV; - if (!count) { - dev_dbg(dev, "No SoundWire Managers detected\n"); - return -EINVAL; - } - dev_dbg(dev, "ACPI reports %d SoundWire Manager devices\n", count); - acp_data->sdw_manager_count = count; - for (index = 0; index < count; index++) { - scnprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index); - link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name); - if (!link) { - dev_err(dev, "Manager node %s not found\n", name); - return -EIO; - } + sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0); + if (!sdw_dev) + return -ENODEV; - ret = fwnode_property_read_u32(link, "amd-sdw-power-mode", &acp_sdw_power_mode); - if (ret) - return ret; - /* - * when SoundWire configuration is selected from acp pin config, - * based on manager instances count, acp init/de-init sequence should be - * executed as part of PM ops only when Bus reset is applied for the active - * SoundWire manager instances. - */ - if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) { - acp_data->acp_reset = false; - return 0; - } - } + acp_data->info.handle = sdw_dev->handle; + acp_data->info.count = AMD_SDW_MAX_MANAGERS; + return amd_sdw_scan_controller(&acp_data->info); +} +#else +static int acp_scan_sdw_devices(struct device *dev, u64 addr) +{ return 0; } +#endif -static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data) +static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) { - struct acpi_device *dmic_dev; - struct acpi_device *sdw_dev; + struct acpi_device *pdm_dev; const union acpi_object *obj; + u32 config; bool is_dmic_dev = false; bool is_sdw_dev = false; int ret; - dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); - if (dmic_dev) { - /* is_dmic_dev flag will be set when ACP PDM controller device exists */ - if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type", - ACPI_TYPE_INTEGER, &obj) && - obj->integer.value == ACP_DMIC_DEV) - is_dmic_dev = true; - } - - sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0); - if (sdw_dev) { - acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev); - ret = sdw_amd_scan_controller(&pci->dev); - /* is_sdw_dev flag will be set when SoundWire Manager device exists */ - if (!ret) - is_sdw_dev = true; - } - if (!is_dmic_dev && !is_sdw_dev) - return -ENODEV; - dev_dbg(&pci->dev, "Audio Mode %d\n", config); + config = readl(acp_data->acp63_base + ACP_PIN_CONFIG); switch (config) { case ACP_CONFIG_4: case ACP_CONFIG_5: case ACP_CONFIG_10: case ACP_CONFIG_11: - if (is_dmic_dev) { - acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_PDM_MODE_DEVS; - } + acp_data->is_pdm_config = true; break; case ACP_CONFIG_2: case ACP_CONFIG_3: - if (is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; - break; - default: - return -EINVAL; - } - } + acp_data->is_sdw_config = true; break; case ACP_CONFIG_6: case ACP_CONFIG_7: @@ -360,40 +289,36 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63 case ACP_CONFIG_8: case ACP_CONFIG_13: case ACP_CONFIG_14: - if (is_dmic_dev && is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS; - break; - default: - return -EINVAL; - } - } else if (is_dmic_dev) { - acp_data->pdev_config = ACP63_PDM_DEV_CONFIG; - acp_data->pdev_count = ACP63_PDM_MODE_DEVS; - } else if (is_sdw_dev) { - switch (acp_data->sdw_manager_count) { - case 1: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_MODE_DEVS; - break; - case 2: - acp_data->pdev_config = ACP63_SDW_DEV_CONFIG; - acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS; - break; - default: - return -EINVAL; - } - } + acp_data->is_pdm_config = true; + acp_data->is_sdw_config = true; break; default: break; } + + if (acp_data->is_pdm_config) { + pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); + if (pdm_dev) { + /* is_dmic_dev flag will be set when ACP PDM controller device exists */ + if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type", + ACPI_TYPE_INTEGER, &obj) && + obj->integer.value == ACP_DMIC_DEV) + is_dmic_dev = true; + } + } + + if (acp_data->is_sdw_config) { + ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR); + if (!ret && acp_data->info.link_mask) + is_sdw_dev = true; + } + + acp_data->is_pdm_dev = is_dmic_dev; + acp_data->is_sdw_dev = is_sdw_dev; + if (!is_dmic_dev && !is_sdw_dev) { + dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); + return -ENODEV; + } return 0; } @@ -576,7 +501,6 @@ static int snd_acp63_probe(struct pci_dev *pci, struct acp63_dev_data *adata; u32 addr; u32 irqflags, flag; - int val; int ret; irqflags = IRQF_SHARED; @@ -637,8 +561,7 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP PCI IRQ request failed\n"); goto de_init; } - val = readl(adata->acp63_base + ACP_PIN_CONFIG); - ret = get_acp63_device_config(val, pci, adata); + ret = get_acp63_device_config(pci, adata); /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */ if (ret) { dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret); @@ -740,4 +663,5 @@ module_pci_driver(ps_acp63_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver"); +MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From eaf825037d6df89811d43391be920bf6ad731463 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 14 Feb 2024 16:10:02 +0530 Subject: ASoC: amd: ps: refactor acp child platform device creation code Refactor ACP child platform device creation code based on acp config. Use common SoundWire manager functions for device probe and exit sequences. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240214104014.1144668-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/acp63.h | 69 +++----------- sound/soc/amd/ps/pci-ps.c | 237 ++++++++++++++++++++-------------------------- 2 files changed, 116 insertions(+), 190 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 123b9ade69d4..b2fd554d50d2 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -11,7 +11,6 @@ #define ACP_DEVICE_ID 0x15E2 #define ACP63_REG_START 0x1240000 #define ACP63_REG_END 0x1250200 -#define ACP63_DEVS 5 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP_PGFSM_CNTL_POWER_ON_MASK 1 @@ -56,32 +55,6 @@ #define ACP_DMIC_DEV 2 -/* ACP63_PDM_MODE_DEVS corresponds to platform devices count for ACP PDM configuration */ -#define ACP63_PDM_MODE_DEVS 3 - -/* - * ACP63_SDW0_MODE_DEVS corresponds to platform devices count for - * SW0 SoundWire manager instance configuration - */ -#define ACP63_SDW0_MODE_DEVS 2 - -/* - * ACP63_SDW0_SDW1_MODE_DEVS corresponds to platform devices count for SW0 + SW1 SoundWire manager - * instances configuration - */ -#define ACP63_SDW0_SDW1_MODE_DEVS 3 - -/* - * ACP63_SDW0_PDM_MODE_DEVS corresponds to platform devices count for SW0 manager - * instance + ACP PDM controller configuration - */ -#define ACP63_SDW0_PDM_MODE_DEVS 4 - -/* - * ACP63_SDW0_SDW1_PDM_MODE_DEVS corresponds to platform devices count for - * SW0 + SW1 SoundWire manager instances + ACP PDM controller configuration - */ -#define ACP63_SDW0_SDW1_PDM_MODE_DEVS 5 #define ACP63_DMIC_ADDR 2 #define ACP63_SDW_ADDR 5 #define AMD_SDW_MAX_MANAGERS 2 @@ -89,17 +62,6 @@ /* time in ms for acp timeout */ #define ACP_TIMEOUT 500 -/* ACP63_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM controller */ -#define ACP63_PDM_DEV_CONFIG BIT(0) - -/* ACP63_SDW_DEV_CONFIG corresponds to platform device configuration for SDW manager instances */ -#define ACP63_SDW_DEV_CONFIG BIT(1) - -/* - * ACP63_SDW_PDM_DEV_CONFIG corresponds to platform device configuration for ACP PDM + SoundWire - * manager instance combination. - */ -#define ACP63_SDW_PDM_DEV_CONFIG GENMASK(1, 0) #define ACP_SDW0_STAT BIT(21) #define ACP_SDW1_STAT BIT(2) #define ACP_ERROR_IRQ BIT(29) @@ -254,21 +216,18 @@ struct sdw_dma_ring_buf_reg { * struct acp63_dev_data - acp pci driver context * @acp63_base: acp mmio base * @res: resource - * @pdev: array of child platform device node structures + * @pdm_dev: ACP PDM controller platform device + * @dmic_codec: platform device for DMIC Codec + * sdw_dma_dev: platform device for SoundWire DMA controller * @acp_lock: used to protect acp common registers - * @sdw_fw_node: SoundWire controller fw node handle - * @pdev_config: platform device configuration - * @pdev_count: platform devices count - * @pdm_dev_index: pdm platform device index - * @sdw_manager_count: SoundWire manager instance count - * @sdw0_dev_index: SoundWire Manager-0 platform device index - * @sdw1_dev_index: SoundWire Manager-1 platform device index - * @sdw_dma_dev_index: SoundWire DMA controller platform device index * @info: SoundWire AMD information found in ACPI tables + * @sdw: SoundWire context for all SoundWire manager instances * @is_sdw_dev: flag set to true when any SoundWire manager instances are available * @is_pdm_dev: flag set to true when ACP PDM controller exists * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS + * @addr: pci ioremap address + * @reg_range: ACP reigister range * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance * @acp_reset: flag set to true when bus reset is applied across all @@ -278,21 +237,19 @@ struct sdw_dma_ring_buf_reg { struct acp63_dev_data { void __iomem *acp63_base; struct resource *res; - struct platform_device *pdev[ACP63_DEVS]; + struct platform_device *pdm_dev; + struct platform_device *dmic_codec_dev; + struct platform_device *sdw_dma_dev; struct mutex acp_lock; /* protect shared registers */ - struct fwnode_handle *sdw_fw_node; - u16 pdev_config; - u16 pdev_count; - u16 pdm_dev_index; - u8 sdw_manager_count; - u16 sdw0_dev_index; - u16 sdw1_dev_index; - u16 sdw_dma_dev_index; struct sdw_amd_acpi_info info; + /* sdw context allocated by SoundWire driver */ + struct sdw_amd_ctx *sdw; bool is_sdw_dev; bool is_pdm_dev; bool is_pdm_config; bool is_sdw_config; + u32 addr; + u32 reg_range; u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; bool acp_reset; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c97e418a88ce..b7cb3f98707f 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -104,10 +104,8 @@ static irqreturn_t acp63_irq_thread(int irq, void *context) struct sdw_dma_dev_data *sdw_dma_data; struct acp63_dev_data *adata = context; u32 stream_index; - u16 pdev_index; - pdev_index = adata->sdw_dma_dev_index; - sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev); for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) { if (adata->sdw0_dma_intr_stat[stream_index]) { @@ -135,7 +133,6 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) u32 stream_id = 0; u16 irq_flag = 0; u16 sdw_dma_irq_flag = 0; - u16 pdev_index; u16 index; adata = dev_id; @@ -149,8 +146,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ext_intr_stat & ACP_SDW0_STAT) { writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT); - pdev_index = adata->sdw0_dev_index; - amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev); if (amd_manager) schedule_work(&amd_manager->amd_sdw_irq_thread); irq_flag = 1; @@ -159,8 +155,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); if (ext_intr_stat1 & ACP_SDW1_STAT) { writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); - pdev_index = adata->sdw1_dev_index; - amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev); if (amd_manager) schedule_work(&amd_manager->amd_sdw_irq_thread); irq_flag = 1; @@ -176,8 +171,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) } if (ext_intr_stat & BIT(PDM_DMA_STAT)) { - pdev_index = adata->pdm_dev_index; - ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev); writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); if (ps_pdm_data->capture_stream) snd_pcm_period_elapsed(ps_pdm_data->capture_stream); @@ -255,11 +249,53 @@ static int acp_scan_sdw_devices(struct device *dev, u64 addr) acp_data->info.count = AMD_SDW_MAX_MANAGERS; return amd_sdw_scan_controller(&acp_data->info); } + +static int amd_sdw_probe(struct device *dev) +{ + struct acp63_dev_data *acp_data; + struct sdw_amd_res sdw_res; + int ret; + + acp_data = dev_get_drvdata(dev); + memset(&sdw_res, 0, sizeof(sdw_res)); + sdw_res.addr = acp_data->addr; + sdw_res.reg_range = acp_data->reg_range; + sdw_res.handle = acp_data->info.handle; + sdw_res.parent = dev; + sdw_res.dev = dev; + sdw_res.acp_lock = &acp_data->acp_lock; + sdw_res.count = acp_data->info.count; + sdw_res.mmio_base = acp_data->acp63_base; + sdw_res.link_mask = acp_data->info.link_mask; + ret = sdw_amd_probe(&sdw_res, &acp_data->sdw); + if (ret) + dev_err(dev, "error: SoundWire probe failed\n"); + return ret; +} + +static int amd_sdw_exit(struct acp63_dev_data *acp_data) +{ + if (acp_data->sdw) + sdw_amd_exit(acp_data->sdw); + acp_data->sdw = NULL; + + return 0; +} #else static int acp_scan_sdw_devices(struct device *dev, u64 addr) { return 0; } + +static int amd_sdw_probe(struct device *dev) +{ + return 0; +} + +static int amd_sdw_exit(struct acp63_dev_data *acp_data) +{ + return 0; +} #endif static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) @@ -343,17 +379,13 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr) { - struct acp_sdw_pdata *sdw_pdata; - struct platform_device_info pdevinfo[ACP63_DEVS]; + struct platform_device_info pdevinfo; struct device *parent; - int index; int ret; parent = &pci->dev; - dev_dbg(&pci->dev, - "%s pdev_config:0x%x pdev_count:0x%x\n", __func__, adata->pdev_config, - adata->pdev_count); - if (adata->pdev_config) { + + if (adata->is_sdw_dev || adata->is_pdm_dev) { adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); if (!adata->res) { ret = -ENOMEM; @@ -365,130 +397,57 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data memset(&pdevinfo, 0, sizeof(pdevinfo)); } - switch (adata->pdev_config) { - case ACP63_PDM_DEV_CONFIG: - adata->pdm_dev_index = 0; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", + if (adata->is_pdm_dev && adata->is_pdm_config) { + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma", 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach", - 0, NULL, 0, NULL, 0); - break; - case ACP63_SDW_DEV_CONFIG: - if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - sdw_pdata->instance = 0; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->sdw0_dev_index = 0; - adata->sdw_dma_dev_index = 1; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - sdw_pdata, sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - } else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - - sdw_pdata[0].instance = 0; - sdw_pdata[1].instance = 1; - sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; - sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->sdw0_dev_index = 0; - adata->sdw1_dev_index = 1; - adata->sdw_dma_dev_index = 2; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 1, adata->res, 1, - &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); + adata->pdm_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->pdm_dev)) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->pdm_dev); + goto de_init; } - break; - case ACP63_SDW_PDM_DEV_CONFIG: - if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata), - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - - sdw_pdata->instance = 0; - sdw_pdata->acp_sdw_lock = &adata->acp_lock; - adata->pdm_dev_index = 0; - adata->sdw0_dev_index = 1; - adata->sdw_dma_dev_index = 2; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - sdw_pdata, sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); - } else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) { - sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2, - GFP_KERNEL); - if (!sdw_pdata) { - ret = -ENOMEM; - goto de_init; - } - sdw_pdata[0].instance = 0; - sdw_pdata[1].instance = 1; - sdw_pdata[0].acp_sdw_lock = &adata->acp_lock; - sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; - adata->pdm_dev_index = 0; - adata->sdw0_dev_index = 1; - adata->sdw1_dev_index = 2; - adata->sdw_dma_dev_index = 3; - acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node, - "amd_sdw_manager", 0, adata->res, 1, - &sdw_pdata[0], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node, - "amd_sdw_manager", 1, adata->res, 1, - &sdw_pdata[1], sizeof(struct acp_sdw_pdata)); - acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma", - 0, adata->res, 1, NULL, 0); - acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec", - 0, NULL, 0, NULL, 0); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec", + 0, NULL, 0, NULL, 0); + adata->dmic_codec_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->dmic_codec_dev)) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->dmic_codec_dev); + goto unregister_pdm_dev; } - break; - default: - dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n"); - return 0; } + if (adata->is_sdw_dev && adata->is_sdw_config) { + ret = amd_sdw_probe(&pci->dev); + if (ret) { + if (adata->is_pdm_dev) + goto unregister_dmic_codec_dev; + else + goto de_init; + } + memset(&pdevinfo, 0, sizeof(pdevinfo)); + acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma", + 0, adata->res, 1, NULL, 0); - for (index = 0; index < adata->pdev_count; index++) { - adata->pdev[index] = platform_device_register_full(&pdevinfo[index]); - if (IS_ERR(adata->pdev[index])) { + adata->sdw_dma_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(adata->sdw_dma_dev)) { dev_err(&pci->dev, - "cannot register %s device\n", pdevinfo[index].name); - ret = PTR_ERR(adata->pdev[index]); - goto unregister_devs; + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(adata->sdw_dma_dev); + if (adata->is_pdm_dev) + goto unregister_dmic_codec_dev; + else + goto de_init; } } + return 0; -unregister_devs: - for (--index; index >= 0; index--) - platform_device_unregister(adata->pdev[index]); +unregister_dmic_codec_dev: + platform_device_unregister(adata->dmic_codec_dev); +unregister_pdm_dev: + platform_device_unregister(adata->pdm_dev); de_init: if (acp63_deinit(adata->acp63_base, &pci->dev)) dev_err(&pci->dev, "ACP de-init failed\n"); @@ -542,6 +501,8 @@ static int snd_acp63_probe(struct pci_dev *pci, ret = -ENOMEM; goto release_regions; } + adata->addr = addr; + adata->reg_range = ACP63_REG_END - ACP63_REG_START; /* * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init() * will be invoked for all ACP configurations during suspend/resume callbacks. @@ -572,6 +533,7 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP platform devices creation failed\n"); goto de_init; } + skip_pdev_creation: device_set_wakeup_enable(&pci->dev, true); pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); @@ -626,11 +588,17 @@ static const struct dev_pm_ops acp63_pm_ops = { static void snd_acp63_remove(struct pci_dev *pci) { struct acp63_dev_data *adata; - int ret, index; + int ret; adata = pci_get_drvdata(pci); - for (index = 0; index < adata->pdev_count; index++) - platform_device_unregister(adata->pdev[index]); + if (adata->sdw) { + amd_sdw_exit(adata); + platform_device_unregister(adata->sdw_dma_dev); + } + if (adata->is_pdm_dev) { + platform_device_unregister(adata->pdm_dev); + platform_device_unregister(adata->dmic_codec_dev); + } ret = acp63_deinit(adata->acp63_base, &pci->dev); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); @@ -663,5 +631,6 @@ module_pci_driver(ps_acp63_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver"); +MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT); MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 3c697ced399cac295c34c9611f05d04f4c951aa9 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 14 Feb 2024 16:10:03 +0530 Subject: ASoC: amd: ps: remove acp_reset flag The earlier acp_reset flag is set to true in two instances as mentioned below. 1. When active SoundWire manager instances power mode is set to Power off mode when SoundWire configuration is selected. 2. For other acp configurations As code being refactored and common function being used for scanning SoundWire controller, acp_reset flag update logic is dropped. Instead of it, check the SoundWire manager instance enable state, based on it update sdw_en_stat flag which will be used to apply ACP init/de-init sequence during suspend/resume callbacks based on flag set value when SoundWire configuration is selected. For other acp configurations, acp init/de-init will be called by default. Refactor existing pm ops logic for SoundWire configuration and use sdw_en_stat flag for invoking acp init/de-init sequence. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240214104014.1144668-3-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/acp63.h | 5 ++--- sound/soc/amd/ps/pci-ps.c | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 21 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index b2fd554d50d2..fb4261f7fa4b 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -226,12 +226,11 @@ struct sdw_dma_ring_buf_reg { * @is_pdm_dev: flag set to true when ACP PDM controller exists * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS + * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled * @addr: pci ioremap address * @reg_range: ACP reigister range * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance - * @acp_reset: flag set to true when bus reset is applied across all - * the active SoundWire manager instances */ struct acp63_dev_data { @@ -248,11 +247,11 @@ struct acp63_dev_data { bool is_pdm_dev; bool is_pdm_config; bool is_sdw_config; + bool sdw_en_stat; u32 addr; u32 reg_range; u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; - bool acp_reset; }; int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index b7cb3f98707f..c141397a2cac 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -503,13 +503,6 @@ static int snd_acp63_probe(struct pci_dev *pci, } adata->addr = addr; adata->reg_range = ACP63_REG_END - ACP63_REG_START; - /* - * By default acp_reset flag is set to true. i.e acp_deinit() and acp_init() - * will be invoked for all ACP configurations during suspend/resume callbacks. - * This flag should be set to false only when SoundWire manager power mode - * set to ClockStopMode. - */ - adata->acp_reset = true; pci_set_master(pci); pci_set_drvdata(pci, adata); mutex_init(&adata->acp_lock); @@ -552,31 +545,46 @@ disable_pci: return ret; } +static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata) +{ + u32 sdw0_en, sdw1_en; + + sdw0_en = readl(adata->acp63_base + ACP_SW0_EN); + sdw1_en = readl(adata->acp63_base + ACP_SW1_EN); + return (sdw0_en || sdw1_en); +} + static int __maybe_unused snd_acp63_suspend(struct device *dev) { struct acp63_dev_data *adata; - int ret = 0; + int ret; adata = dev_get_drvdata(dev); - if (adata->acp_reset) { - ret = acp63_deinit(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP de-init failed\n"); + if (adata->is_sdw_dev) { + adata->sdw_en_stat = check_acp_sdw_enable_status(adata); + if (adata->sdw_en_stat) + return 0; } + ret = acp63_deinit(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + return ret; } static int __maybe_unused snd_acp63_resume(struct device *dev) { struct acp63_dev_data *adata; - int ret = 0; + int ret; adata = dev_get_drvdata(dev); - if (adata->acp_reset) { - ret = acp63_init(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP init failed\n"); - } + if (adata->sdw_en_stat) + return 0; + + ret = acp63_init(adata->acp63_base, dev); + if (ret) + dev_err(dev, "ACP init failed\n"); + return ret; } -- cgit v1.2.3 From c76f3b1f9b9ae20f8c944bb01c395329d525a917 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 14 Feb 2024 16:10:04 +0530 Subject: ASoC: amd: ps: fix for acp pme wake for soundwire configuration Consider the below scenario, When ACP and SoundWire managers are in D3 state and SoundWire manager power off mode is selected and acp and SoundWire manager instances are in runtime suspended state. In this case, for the ACP PME wake event, the ACP PCI driver should resume SoundWire manager devices based on wake enable status set. Add code for handling ACP PME wake event for runtime suspend scenario when SoundWire power off mode is selected. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240214104014.1144668-4-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/pci-ps.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c141397a2cac..c42660245019 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -554,6 +554,19 @@ static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata) return (sdw0_en || sdw1_en); } +static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata) +{ + u32 val; + + val = readl(adata->acp63_base + ACP_SW0_WAKE_EN); + if (val && adata->sdw->pdev[0]) + pm_request_resume(&adata->sdw->pdev[0]->dev); + + val = readl(adata->acp63_base + ACP_SW1_WAKE_EN); + if (val && adata->sdw->pdev[1]) + pm_request_resume(&adata->sdw->pdev[1]->dev); +} + static int __maybe_unused snd_acp63_suspend(struct device *dev) { struct acp63_dev_data *adata; @@ -572,6 +585,26 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev) return ret; } +static int __maybe_unused snd_acp63_runtime_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->sdw_en_stat) + return 0; + + ret = acp63_init(adata->acp63_base, dev); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; + } + + if (!adata->sdw_en_stat) + handle_acp63_sdw_pme_event(adata); + return 0; +} + static int __maybe_unused snd_acp63_resume(struct device *dev) { struct acp63_dev_data *adata; @@ -589,7 +622,7 @@ static int __maybe_unused snd_acp63_resume(struct device *dev) } static const struct dev_pm_ops acp63_pm_ops = { - SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_resume, NULL) + SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume) }; -- cgit v1.2.3 From bbf3e6145ea09cf346ce09146b0b98415095f170 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 14 Feb 2024 16:10:05 +0530 Subject: ASoC: amd: ps: add machine select and register code Add machine select logic for SoundWire interface and create a machine device node based on ACP PDM/SoundWire configuration. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240214104014.1144668-5-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/acp63.h | 4 +++ sound/soc/amd/ps/pci-ps.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index fb4261f7fa4b..65433184d03e 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -219,9 +219,11 @@ struct sdw_dma_ring_buf_reg { * @pdm_dev: ACP PDM controller platform device * @dmic_codec: platform device for DMIC Codec * sdw_dma_dev: platform device for SoundWire DMA controller + * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration * @acp_lock: used to protect acp common registers * @info: SoundWire AMD information found in ACPI tables * @sdw: SoundWire context for all SoundWire manager instances + * @machine: ACPI machines for SoundWire interface * @is_sdw_dev: flag set to true when any SoundWire manager instances are available * @is_pdm_dev: flag set to true when ACP PDM controller exists * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS @@ -239,10 +241,12 @@ struct acp63_dev_data { struct platform_device *pdm_dev; struct platform_device *dmic_codec_dev; struct platform_device *sdw_dma_dev; + struct platform_device *mach_dev; struct mutex acp_lock; /* protect shared registers */ struct sdw_amd_acpi_info info; /* sdw context allocated by SoundWire driver */ struct sdw_amd_ctx *sdw; + struct snd_soc_acpi_mach *machines; bool is_sdw_dev; bool is_pdm_dev; bool is_pdm_config; diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c42660245019..205bca95aa06 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -17,6 +17,7 @@ #include #include #include +#include "../mach-config.h" #include "acp63.h" @@ -281,6 +282,42 @@ static int amd_sdw_exit(struct acp63_dev_data *acp_data) return 0; } + +static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) +{ + struct snd_soc_acpi_mach *mach; + const struct snd_soc_acpi_link_adr *link; + struct acp63_dev_data *acp_data = dev_get_drvdata(dev); + int ret, i; + + if (acp_data->info.count) { + ret = sdw_amd_get_slave_info(acp_data->sdw); + if (ret) { + dev_dbg(dev, "failed to read slave information\n"); + return NULL; + } + for (mach = acp_data->machines; mach; mach++) { + if (!mach->links) + break; + link = mach->links; + for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) { + if (!snd_soc_acpi_sdw_link_slaves_found(dev, link, + acp_data->sdw->ids, + acp_data->sdw->num_slaves)) + break; + } + if (i == acp_data->info.count || !link->num_adr) + break; + } + if (mach && mach->link_mask) { + mach->mach_params.links = mach->links; + mach->mach_params.link_mask = mach->link_mask; + return mach; + } + } + dev_dbg(dev, "No SoundWire machine driver found\n"); + return NULL; +} #else static int acp_scan_sdw_devices(struct device *dev, u64 addr) { @@ -296,8 +333,44 @@ static int amd_sdw_exit(struct acp63_dev_data *acp_data) { return 0; } + +static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) +{ + return NULL; +} #endif +static int acp63_machine_register(struct device *dev) +{ + struct snd_soc_acpi_mach *mach; + struct acp63_dev_data *adata = dev_get_drvdata(dev); + int size; + + if (adata->is_sdw_dev && adata->is_sdw_config) { + size = sizeof(*adata->machines); + mach = acp63_sdw_machine_select(dev); + if (mach) { + adata->mach_dev = platform_device_register_data(dev, mach->drv_name, + PLATFORM_DEVID_NONE, mach, + size); + if (IS_ERR(adata->mach_dev)) { + dev_err(dev, + "cannot register Machine device for SoundWire Interface\n"); + return PTR_ERR(adata->mach_dev); + } + } + + } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) { + adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach", + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(adata->mach_dev)) { + dev_err(dev, "cannot register amd_ps_mach device\n"); + return PTR_ERR(adata->mach_dev); + } + } + return 0; +} + static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) { struct acpi_device *pdm_dev; @@ -526,7 +599,11 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP platform devices creation failed\n"); goto de_init; } - + ret = acp63_machine_register(&pci->dev); + if (ret) { + dev_err(&pci->dev, "ACP machine register failed\n"); + goto de_init; + } skip_pdev_creation: device_set_wakeup_enable(&pci->dev, true); pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS); @@ -640,6 +717,8 @@ static void snd_acp63_remove(struct pci_dev *pci) platform_device_unregister(adata->pdm_dev); platform_device_unregister(adata->dmic_codec_dev); } + if (adata->mach_dev) + platform_device_unregister(adata->mach_dev); ret = acp63_deinit(adata->acp63_base, &pci->dev); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); -- cgit v1.2.3 From a3d543b9e6599fbbb9efc1876919627960c5e97a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 19 Feb 2024 10:38:45 +0100 Subject: ASoC: SOF: amd: fix soundwire dependencies The soundwire-amd driver has a bit of a layering violation requiring the SOF driver to directly call into its exported symbols rather than through an abstraction. The SND_SOC_SOF_AMD_SOUNDWIRE Kconfig symbol tries to deal with the dependency by selecting SOUNDWIRE_AMD in a complicated set of conditions, but gets it wrong for a configuration involving SND_SOC_SOF_AMD_COMMON=y, SND_SOC_SOF_AMD_ACP63=m, and SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE=m SOUNDWIRE_AMD=m, which results in a link failure: ld.lld: error: undefined symbol: sdw_amd_get_slave_info >>> referenced by acp-common.c ld.lld: error: undefined symbol: amd_sdw_scan_controller ld.lld: error: undefined symbol: sdw_amd_probe ld.lld: error: undefined symbol: sdw_amd_exit >>> referenced by acp.c >>> sound/soc/sof/amd/acp.o:(amd_sof_acp_remove) in archive vmlinux.a In essence, the SND_SOC_SOF_AMD_COMMON option cannot be built-in when trying to link against a modular SOUNDWIRE_AMD driver. Since CONFIG_SOUNDWIRE_AMD is a user-visible option, it really should never be selected by another driver in the first place, so replace the extra complexity with a normal Kconfig dependency in SND_SOC_SOF_AMD_SOUNDWIRE, plus a top-level check that forbids any of the AMD SOF drivers from being built-in with CONFIG_SOUNDWIRE_AMD=m. In normal configs, they should all either be built-in or all loadable modules anyway, so this simplification does not limit any real usecases. Fixes: d948218424bf ("ASoC: SOF: amd: add code for invoking soundwire manager helper functions") Signed-off-by: Arnd Bergmann Link: https://msgid.link/r/20240219093900.644574-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/sof/amd/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index c3bbe6c70fb2..2729c6eb3feb 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" + depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD depends on X86 || COMPILE_TEST help This adds support for Sound Open Firmware for AMD platforms. @@ -62,15 +63,14 @@ config SND_SOC_SOF_ACP_PROBES config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE tristate - select SOUNDWIRE_AMD if SND_SOC_SOF_AMD_SOUNDWIRE != n select SND_AMD_SOUNDWIRE_ACPI if ACPI config SND_SOC_SOF_AMD_SOUNDWIRE tristate "SOF support for SoundWire based AMD platforms" default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE - depends on ACPI && SOUNDWIRE - depends on !(SOUNDWIRE=m && SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE=y) + depends on ACPI + depends on SOUNDWIRE_AMD help This adds support for SoundWire with Sound Open Firmware for AMD platforms. -- cgit v1.2.3 From e480c0991db00b24b39010bfd56eda5ec2417186 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Feb 2024 14:22:19 +0000 Subject: ASoC: tas2781: Remove redundant initialization of pointer 'data' The pointer 'data' being initialized with a value that is never read, it is being re-assigned inside a while-loop. The initialization is redundant and can be removed. Cleans up clang scan build warning sound/soc/codecs/tas2781-fmwlib.c:1534:17: warning: Value stored to 'data' during its initialization is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://msgid.link/r/20240216142219.2109050-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-fmwlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 85e14ff61769..45760fe19523 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -1531,7 +1531,7 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv, unsigned int sleep_time; unsigned int len; unsigned int nr_cmds; - unsigned char *data = block->data; + unsigned char *data; unsigned char crc_chksum = 0; unsigned char offset; unsigned char book; -- cgit v1.2.3 From 3b4ec34602c562fa8fa59dd8545ac7f3cdfc235e Mon Sep 17 00:00:00 2001 From: Vitaly Rodionov Date: Fri, 16 Feb 2024 10:11:57 +0000 Subject: ASoC: cs42l42: Remove redundant delays in suspend(). This patch will remove redundant delay and minimise total suspend() function call time. Signed-off-by: Vitaly Rodionov Link: https://msgid.link/r/20240216101157.23176-1-vitalyr@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs42l42.h | 5 ++--- sound/soc/codecs/cs42l42.c | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/cs42l42.h b/include/sound/cs42l42.h index 3994e933db19..1bd8eee54f66 100644 --- a/include/sound/cs42l42.h +++ b/include/sound/cs42l42.h @@ -809,8 +809,7 @@ #define CS42L42_PLL_LOCK_TIMEOUT_US 1250 #define CS42L42_HP_ADC_EN_TIME_US 20000 #define CS42L42_PDN_DONE_POLL_US 1000 -#define CS42L42_PDN_DONE_TIMEOUT_US 200000 -#define CS42L42_PDN_DONE_TIME_MS 100 -#define CS42L42_FILT_DISCHARGE_TIME_MS 46 +#define CS42L42_PDN_DONE_TIMEOUT_US 235000 +#define CS42L42_PDN_DONE_TIME_MS 65 #endif /* __CS42L42_H */ diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 2d11c5125f73..60d366e53526 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2195,7 +2195,6 @@ int cs42l42_suspend(struct device *dev) /* Discharge FILT+ */ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK); - msleep(CS42L42_FILT_DISCHARGE_TIME_MS); regcache_cache_only(cs42l42->regmap, true); gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); -- cgit v1.2.3 From 1b72943ab1159ad27c11a302644fabb8bc2881bb Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:26 +0100 Subject: ASoC: Intel: avs: L1SEN reference counted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code loading is not the only procedure that manipulates L1SEN. Update existing mechanism so the stream starting procedure can interfere with L1SEN without causing any trouble to its other users. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 1 + sound/soc/intel/avs/core.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index d694e08e44e1..cb4302816e74 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -127,6 +127,7 @@ struct avs_dev { int *core_refs; /* reference count per core */ char **lib_names; int num_lp_paths; + atomic_t l1sen_counter; /* controls whether L1SEN should be disabled */ struct completion fw_ready; struct work_struct probe_work; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index db78eb2f0108..30baaa5de016 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -69,9 +69,14 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable) void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) { - u32 value = enable ? AZX_VS_EM2_L1SEN : 0; - - snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value); + if (enable) { + if (atomic_inc_and_test(&adev->l1sen_counter)) + snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, + AZX_VS_EM2_L1SEN); + } else { + if (atomic_dec_return(&adev->l1sen_counter) == -1) + snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, 0); + } } static int avs_hdac_bus_init_streams(struct hdac_bus *bus) -- cgit v1.2.3 From e1a0cbae52d0bf3cb350eba5c95c46c14a5bcda4 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:27 +0100 Subject: ASoC: Intel: avs: Fix sound clipping in single capture scenario MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid sound clipping when there just one, single CAPTURE stream ongoing, disable L1SEN before it is started. Any PLAYBACK stream or additional CAPTURE allows L1SEN to be re-enabled. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/pcm.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 4dfc5a1ebb7c..2cafbc392cdb 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -643,6 +643,79 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so return 0; } +static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream) +{ + struct hdac_stream *first_running = NULL; + struct hdac_stream *pos; + struct avs_dev *adev = hdac_to_avs(bus); + + list_for_each_entry(pos, &bus->stream_list, list) { + if (pos->running) { + if (first_running) + break; /* more than one running */ + first_running = pos; + } + } + + /* + * If host_stream is a CAPTURE stream and will be the only one running, + * disable L1SEN to avoid sound clipping. + */ + if (!first_running) { + if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, false); + snd_hdac_stream_start(hdac_stream(host_stream)); + return; + } + + snd_hdac_stream_start(hdac_stream(host_stream)); + /* + * If host_stream is the first stream to break the rule above, + * re-enable L1SEN. + */ + if (list_entry_is_head(pos, &bus->stream_list, list) && + first_running->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, true); +} + +static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream) +{ + struct hdac_stream *first_running = NULL; + struct hdac_stream *pos; + struct avs_dev *adev = hdac_to_avs(bus); + + list_for_each_entry(pos, &bus->stream_list, list) { + if (pos == hdac_stream(host_stream)) + continue; /* ignore stream that is about to be stopped */ + if (pos->running) { + if (first_running) + break; /* more than one running */ + first_running = pos; + } + } + + /* + * If host_stream is a CAPTURE stream and is the only one running, + * re-enable L1SEN. + */ + if (!first_running) { + snd_hdac_stream_stop(hdac_stream(host_stream)); + if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, true); + return; + } + + /* + * If by stopping host_stream there is only a single, CAPTURE stream running + * left, disable L1SEN to avoid sound clipping. + */ + if (list_entry_is_head(pos, &bus->stream_list, list) && + first_running->direction == SNDRV_PCM_STREAM_CAPTURE) + avs_hda_l1sen_enable(adev, false); + + snd_hdac_stream_stop(hdac_stream(host_stream)); +} + static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -664,7 +737,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_start(hdac_stream(host_stream)); + avs_hda_stream_start(bus, host_stream); spin_unlock_irqrestore(&bus->reg_lock, flags); /* Timeout on DRSM poll shall not stop the resume so ignore the result. */ @@ -694,7 +767,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru dev_err(dai->dev, "pause FE path failed: %d\n", ret); spin_lock_irqsave(&bus->reg_lock, flags); - snd_hdac_stream_stop(hdac_stream(host_stream)); + avs_hda_stream_stop(bus, host_stream); spin_unlock_irqrestore(&bus->reg_lock, flags); ret = avs_path_reset(data->path); -- cgit v1.2.3 From a8f858d98f016a0209edaf1518fd45a5e5c62d47 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:28 +0100 Subject: ASoC: Intel: avs: Prefix SKL/APL-specific members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefix members that are platform-specific with 'avs_' to improve code cohesiveness and reduce the chance for naming-conflics with other drivers. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 51 +++++++++++++++++++++--------------------- sound/soc/intel/avs/avs.h | 18 +++++++-------- sound/soc/intel/avs/core.c | 4 ++-- sound/soc/intel/avs/messages.h | 10 ++++----- sound/soc/intel/avs/skl.c | 30 ++++++++++++------------- 5 files changed, 56 insertions(+), 57 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 1860099c782a..24c06568b3e8 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -14,10 +14,10 @@ #include "topology.h" static int __maybe_unused -apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { - struct apl_log_state_info *info; + struct avs_apl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; int ret, i; @@ -48,9 +48,9 @@ apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri return 0; } -static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { - struct apl_log_buffer_layout layout; + struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; addr = avs_log_buffer_addr(adev, msg->log.core); @@ -63,11 +63,11 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg /* consume the logs regardless of consumer presence */ goto update_read_ptr; - buf = apl_log_payload_addr(addr); + buf = avs_apl_log_payload_addr(addr); if (layout.read_ptr > layout.write_ptr) { avs_dump_fw_log(adev, buf + layout.read_ptr, - apl_log_payload_size(adev) - layout.read_ptr); + avs_apl_log_payload_size(adev) - layout.read_ptr); layout.read_ptr = 0; } avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr); @@ -77,7 +77,8 @@ update_read_ptr: return 0; } -static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout) +static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core, + struct avs_apl_log_buffer_layout *layout) { unsigned long timeout; void __iomem *addr; @@ -99,11 +100,11 @@ static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buf } /* reads log header and tests its type */ -#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) +#define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) -static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) { - struct apl_log_buffer_layout layout; + struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; size_t dump_size; u16 offset = 0; @@ -124,9 +125,9 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) if (!addr) goto exit; - buf = apl_log_payload_addr(addr); + buf = avs_apl_log_payload_addr(addr); memcpy_fromio(&layout, addr, sizeof(layout)); - if (!apl_is_entry_stackdump(buf + layout.read_ptr)) { + if (!avs_apl_is_entry_stackdump(buf + layout.read_ptr)) { union avs_notify_msg lbs_msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); /* @@ -142,11 +143,11 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) do { u32 count; - if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout)) + if (avs_apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout)) break; if (layout.read_ptr > layout.write_ptr) { - count = apl_log_payload_size(adev) - layout.read_ptr; + count = avs_apl_log_payload_size(adev) - layout.read_ptr; memcpy_fromio(pos + offset, buf + layout.read_ptr, count); layout.read_ptr = 0; offset += count; @@ -165,7 +166,7 @@ exit: return 0; } -static bool apl_lp_streaming(struct avs_dev *adev) +static bool avs_apl_lp_streaming(struct avs_dev *adev) { struct avs_path *path; @@ -201,7 +202,7 @@ static bool apl_lp_streaming(struct avs_dev *adev) return true; } -static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +static bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { /* wake in all cases */ if (wake) @@ -215,10 +216,10 @@ static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool w * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition, * not the power-gating mechanism known from cAVS 2.0. */ - return apl_lp_streaming(adev); + return avs_apl_lp_streaming(adev); } -static int apl_set_d0ix(struct avs_dev *adev, bool enable) +static int avs_apl_set_d0ix(struct avs_dev *adev, bool enable) { bool streaming = false; int ret; @@ -231,7 +232,7 @@ static int apl_set_d0ix(struct avs_dev *adev, bool enable) return AVS_IPC_RET(ret); } -const struct avs_dsp_ops apl_dsp_ops = { +const struct avs_dsp_ops avs_apl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, @@ -241,10 +242,10 @@ const struct avs_dsp_ops apl_dsp_ops = { .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, - .log_buffer_offset = skl_log_buffer_offset, - .log_buffer_status = apl_log_buffer_status, - .coredump = apl_coredump, - .d0ix_toggle = apl_d0ix_toggle, - .set_d0ix = apl_set_d0ix, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_apl_d0ix_toggle, + .set_d0ix = avs_apl_set_d0ix, AVS_SET_ENABLE_LOGS_OP(apl) }; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index cb4302816e74..cda3cb7db22a 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -64,8 +64,8 @@ struct avs_dsp_ops { #define avs_dsp_op(adev, op, ...) \ ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) -extern const struct avs_dsp_ops skl_dsp_ops; -extern const struct avs_dsp_ops apl_dsp_ops; +extern const struct avs_dsp_ops avs_skl_dsp_ops; +extern const struct avs_dsp_ops avs_apl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) @@ -249,7 +249,7 @@ void avs_ipc_block(struct avs_ipc *ipc); int avs_dsp_disable_d0ix(struct avs_dev *adev); int avs_dsp_enable_d0ix(struct avs_dev *adev); -int skl_log_buffer_offset(struct avs_dev *adev, u32 core); +int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core); /* Firmware resources management */ @@ -343,21 +343,21 @@ static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_n return ret; } -struct apl_log_buffer_layout { +struct avs_apl_log_buffer_layout { u32 read_ptr; u32 write_ptr; u8 buffer[]; } __packed; -#define apl_log_payload_size(adev) \ - (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout)) +#define avs_apl_log_payload_size(adev) \ + (avs_log_buffer_size(adev) - sizeof(struct avs_apl_log_buffer_layout)) -#define apl_log_payload_addr(addr) \ - (addr + sizeof(struct apl_log_buffer_layout)) +#define avs_apl_log_payload_addr(addr) \ + (addr + sizeof(struct avs_apl_log_buffer_layout)) #ifdef CONFIG_DEBUG_FS #define AVS_SET_ENABLE_LOGS_OP(name) \ - .enable_logs = name##_enable_logs + .enable_logs = avs_##name##_enable_logs bool avs_logging_fw(struct avs_dev *adev); void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len); diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 30baaa5de016..60667d4db7f8 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -738,7 +738,7 @@ static const struct avs_spec skl_desc = { .hotfix = 0, .build = 4732, }, - .dsp_ops = &skl_dsp_ops, + .dsp_ops = &avs_skl_dsp_ops, .core_init_mask = 1, .attributes = AVS_PLATATTR_CLDMA, .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET, @@ -754,7 +754,7 @@ static const struct avs_spec apl_desc = { .hotfix = 1, .build = 4323, }, - .dsp_ops = &apl_dsp_ops, + .dsp_ops = &avs_apl_dsp_ops, .core_init_mask = 3, .attributes = AVS_PLATATTR_IMR, .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET, diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index d0344e242e5b..0f0862818f02 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -359,21 +359,21 @@ enum avs_skl_log_priority { AVS_SKL_LOG_VERBOSE, }; -struct skl_log_state { +struct avs_skl_log_state { u32 enable; u32 min_priority; } __packed; -struct skl_log_state_info { +struct avs_skl_log_state_info { u32 core_mask; - struct skl_log_state logs_core[]; + struct avs_skl_log_state logs_core[]; } __packed; -struct apl_log_state_info { +struct avs_apl_log_state_info { u32 aging_timer_period; u32 fifo_full_timer_period; u32 core_mask; - struct skl_log_state logs_core[]; + struct avs_skl_log_state logs_core[]; } __packed; int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size); diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index 6bb8bbc70442..7ea8d91b54d2 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -13,10 +13,10 @@ #include "messages.h" static int __maybe_unused -skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { - struct skl_log_state_info *info; + struct avs_skl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; int ret, i; @@ -45,7 +45,7 @@ skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_peri return 0; } -int skl_log_buffer_offset(struct avs_dev *adev, u32 core) +int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core) { return core * avs_log_buffer_size(adev); } @@ -53,8 +53,7 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core) /* fw DbgLogWp registers */ #define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core) -static int -skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { void __iomem *buf; u16 size, write, offset; @@ -74,7 +73,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) return 0; } -static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +static int avs_skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) { u8 *dump; @@ -88,20 +87,19 @@ static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) return 0; } -static bool -skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +static bool avs_skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { /* unsupported on cAVS 1.5 hw */ return false; } -static int skl_set_d0ix(struct avs_dev *adev, bool enable) +static int avs_skl_set_d0ix(struct avs_dev *adev, bool enable) { /* unsupported on cAVS 1.5 hw */ return 0; } -const struct avs_dsp_ops skl_dsp_ops = { +const struct avs_dsp_ops avs_skl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, @@ -111,10 +109,10 @@ const struct avs_dsp_ops skl_dsp_ops = { .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, .transfer_mods = avs_cldma_transfer_modules, - .log_buffer_offset = skl_log_buffer_offset, - .log_buffer_status = skl_log_buffer_status, - .coredump = skl_coredump, - .d0ix_toggle = skl_d0ix_toggle, - .set_d0ix = skl_set_d0ix, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_skl_log_buffer_status, + .coredump = avs_skl_coredump, + .d0ix_toggle = avs_skl_d0ix_toggle, + .set_d0ix = avs_skl_set_d0ix, AVS_SET_ENABLE_LOGS_OP(skl) }; -- cgit v1.2.3 From 7576e2f4d99df6efabb77f52b9539fd345233aee Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:29 +0100 Subject: ASoC: Intel: avs: Abstract IPC handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Servicing IPCs on CNL platforms and onward differs from the existing one. To make room for these, enrich platform descriptor with fields representing crucial IPC registers and utilize them throughout the code. While cleaning up device descriptors, reduce the number of code lines by assigning 'min_fw_version' within a single line. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/avs.h | 22 ++++++++++++++++--- sound/soc/intel/avs/core.c | 47 +++++++++++++++++++++++++---------------- sound/soc/intel/avs/ipc.c | 36 +++++++++++++++++-------------- sound/soc/intel/avs/loader.c | 2 +- sound/soc/intel/avs/registers.h | 6 +++--- 5 files changed, 72 insertions(+), 41 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index cda3cb7db22a..6d44981c5c61 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -73,6 +73,23 @@ extern const struct avs_dsp_ops avs_apl_dsp_ops; #define avs_platattr_test(adev, attr) \ ((adev)->spec->attributes & AVS_PLATATTR_##attr) +struct avs_sram_spec { + const u32 base_offset; + const u32 window_size; + const u32 rom_status_offset; +}; + +struct avs_hipc_spec { + const u32 req_offset; + const u32 req_ext_offset; + const u32 req_busy_mask; + const u32 ack_offset; + const u32 ack_done_mask; + const u32 rsp_offset; + const u32 rsp_busy_mask; + const u32 ctl_offset; +}; + /* Platform specific descriptor */ struct avs_spec { const char *name; @@ -82,9 +99,8 @@ struct avs_spec { const u32 core_init_mask; /* used during DSP boot */ const u64 attributes; /* bitmask of AVS_PLATATTR_* */ - const u32 sram_base_offset; - const u32 sram_window_size; - const u32 rom_status; + const struct avs_sram_spec *sram; + const struct avs_hipc_spec *hipc; }; struct avs_fw_entry { diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 60667d4db7f8..15755614509a 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -730,36 +730,47 @@ static const struct dev_pm_ops avs_dev_pm = { SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) }; +static const struct avs_sram_spec skl_sram_spec = { + .base_offset = SKL_ADSP_SRAM_BASE_OFFSET, + .window_size = SKL_ADSP_SRAM_WINDOW_SIZE, + .rom_status_offset = SKL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_sram_spec apl_sram_spec = { + .base_offset = APL_ADSP_SRAM_BASE_OFFSET, + .window_size = APL_ADSP_SRAM_WINDOW_SIZE, + .rom_status_offset = APL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_hipc_spec skl_hipc_spec = { + .req_offset = SKL_ADSP_REG_HIPCI, + .req_ext_offset = SKL_ADSP_REG_HIPCIE, + .req_busy_mask = SKL_ADSP_HIPCI_BUSY, + .ack_offset = SKL_ADSP_REG_HIPCIE, + .ack_done_mask = SKL_ADSP_HIPCIE_DONE, + .rsp_offset = SKL_ADSP_REG_HIPCT, + .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY, + .ctl_offset = SKL_ADSP_REG_HIPCCTL, +}; + static const struct avs_spec skl_desc = { .name = "skl", - .min_fw_version = { - .major = 9, - .minor = 21, - .hotfix = 0, - .build = 4732, - }, + .min_fw_version = { 9, 21, 0, 4732 }, .dsp_ops = &avs_skl_dsp_ops, .core_init_mask = 1, .attributes = AVS_PLATATTR_CLDMA, - .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET, - .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE, - .rom_status = SKL_ADSP_SRAM_BASE_OFFSET, + .sram = &skl_sram_spec, + .hipc = &skl_hipc_spec, }; static const struct avs_spec apl_desc = { .name = "apl", - .min_fw_version = { - .major = 9, - .minor = 22, - .hotfix = 1, - .build = 4323, - }, + .min_fw_version = { 9, 22, 1, 4323 }, .dsp_ops = &avs_apl_dsp_ops, .core_init_mask = 3, .attributes = AVS_PLATATTR_IMR, - .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET, - .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE, - .rom_status = APL_ADSP_SRAM_BASE_OFFSET, + .sram = &apl_sram_spec, + .hipc = &skl_hipc_spec, }; static const struct pci_device_id avs_ids[] = { diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 65bfc83bd1f0..29c7f508a7d6 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -305,6 +305,7 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) { struct avs_dev *adev = dev_id; struct avs_ipc *ipc = adev->ipc; + const struct avs_spec *const spec = adev->spec; u32 adspis, hipc_rsp, hipc_ack; irqreturn_t ret = IRQ_NONE; @@ -312,35 +313,35 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) if (adspis == UINT_MAX || !(adspis & AVS_ADSP_ADSPIS_IPC)) return ret; - hipc_ack = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCIE); - hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); /* DSP acked host's request */ - if (hipc_ack & SKL_ADSP_HIPCIE_DONE) { + if (hipc_ack & spec->hipc->ack_done_mask) { /* * As an extra precaution, mask done interrupt. Code executed * due to complete() found below does not assume any masking. */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_DONE, 0); complete(&ipc->done_completion); /* tell DSP it has our attention */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCIE, - SKL_ADSP_HIPCIE_DONE, - SKL_ADSP_HIPCIE_DONE); + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, + spec->hipc->ack_done_mask, + spec->hipc->ack_done_mask); /* unmask done interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_DONE, AVS_ADSP_HIPCCTL_DONE); ret = IRQ_HANDLED; } /* DSP sent new response to process */ - if (hipc_rsp & SKL_ADSP_HIPCT_BUSY) { + if (hipc_rsp & spec->hipc->rsp_busy_mask) { /* mask busy interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, AVS_ADSP_HIPCCTL_BUSY, 0); ret = IRQ_WAKE_THREAD; @@ -379,10 +380,11 @@ irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) static bool avs_ipc_is_busy(struct avs_ipc *ipc) { struct avs_dev *adev = to_avs_dev(ipc->dev); + const struct avs_spec *const spec = adev->spec; u32 hipc_rsp; - hipc_rsp = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); - return hipc_rsp & SKL_ADSP_HIPCT_BUSY; + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); + return hipc_rsp & spec->hipc->rsp_busy_mask; } static int avs_ipc_wait_busy_completion(struct avs_ipc *ipc, int timeout) @@ -440,9 +442,10 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs) { + const struct avs_spec *const spec = adev->spec; u64 reg = ULONG_MAX; - tx->header |= SKL_ADSP_HIPCI_BUSY; + tx->header |= spec->hipc->req_busy_mask; if (read_fwregs) reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); @@ -450,8 +453,8 @@ static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool r if (tx->size) memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); - snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCIE, tx->header >> 32); - snd_hdac_adsp_writel(adev, SKL_ADSP_REG_HIPCI, tx->header & UINT_MAX); + snd_hdac_adsp_writel(adev, spec->hipc->req_ext_offset, tx->header >> 32); + snd_hdac_adsp_writel(adev, spec->hipc->req_offset, tx->header & UINT_MAX); } static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, @@ -606,6 +609,7 @@ int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, cons void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) { + const struct avs_spec *const spec = adev->spec; u32 value, mask; /* @@ -617,7 +621,7 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable) mask = AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY; value = enable ? mask : 0; - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, mask, value); + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, mask, value); } int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index e83ce6a35755..8e34d3536082 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -306,7 +306,7 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) } /* await ROM init */ - ret = snd_hdac_adsp_readq_poll(adev, spec->rom_status, reg, + ret = snd_hdac_adsp_readq_poll(adev, spec->sram->rom_status_offset, reg, (reg & 0xF) == AVS_ROM_INIT_DONE || (reg & 0xF) == APL_ROM_FW_ENTERED, AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 078a0ebafa42..8468acd15c3d 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -57,7 +57,7 @@ #define APL_ADSP_SRAM_WINDOW_SIZE 0x20000 /* Constants used when accessing SRAM, space shared with firmware */ -#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram_base_offset) +#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram->base_offset) #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) #define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) @@ -72,8 +72,8 @@ /* registry I/O helpers */ #define avs_sram_offset(adev, window_idx) \ - ((adev)->spec->sram_base_offset + \ - (adev)->spec->sram_window_size * (window_idx)) + ((adev)->spec->sram->base_offset + \ + (adev)->spec->sram->window_size * (window_idx)) #define avs_sram_addr(adev, window_idx) \ ((adev)->dsp_ba + avs_sram_offset(adev, window_idx)) -- cgit v1.2.3 From 97bd565ff5a2fc89d302f9919fde37fadf51b645 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:30 +0100 Subject: ASoC: Intel: avs: Abstract IRQ handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Servicing IPCs on CNL platforms and onward differs from the existing one. To make room for these, relocate SKL-based platforms specific code into the skl.c file leaving only the genering irq_handler in the common code. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/apl.c | 4 ++-- sound/soc/intel/avs/avs.h | 8 ++++---- sound/soc/intel/avs/core.c | 14 ++++++++++++++ sound/soc/intel/avs/ipc.c | 30 +----------------------------- sound/soc/intel/avs/skl.c | 29 +++++++++++++++++++++++++++-- 5 files changed, 48 insertions(+), 37 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 24c06568b3e8..6382543a9cdb 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -236,8 +236,8 @@ const struct avs_dsp_ops avs_apl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_dsp_irq_handler, - .irq_thread = avs_dsp_irq_thread, + .irq_handler = avs_irq_handler, + .irq_thread = avs_skl_irq_thread, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 6d44981c5c61..3d823880f965 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -46,8 +46,8 @@ struct avs_dsp_ops { int (* const power)(struct avs_dev *, u32, bool); int (* const reset)(struct avs_dev *, u32, bool); int (* const stall)(struct avs_dev *, u32, bool); - irqreturn_t (* const irq_handler)(int, void *); - irqreturn_t (* const irq_thread)(int, void *); + irqreturn_t (* const irq_handler)(struct avs_dev *); + irqreturn_t (* const irq_thread)(struct avs_dev *); void (* const int_control)(struct avs_dev *, bool); int (* const load_basefw)(struct avs_dev *, struct firmware *); int (* const load_lib)(struct avs_dev *, struct firmware *, u32); @@ -242,8 +242,7 @@ struct avs_ipc { #define AVS_IPC_RET(ret) \ (((ret) <= 0) ? (ret) : -AVS_EIPC) -irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id); -irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id); +irqreturn_t avs_irq_handler(struct avs_dev *adev); void avs_dsp_process_response(struct avs_dev *adev, u64 header); int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, struct avs_ipc_msg *reply, int timeout, const char *name); @@ -265,6 +264,7 @@ void avs_ipc_block(struct avs_ipc *ipc); int avs_dsp_disable_d0ix(struct avs_dev *adev); int avs_dsp_enable_d0ix(struct avs_dev *adev); +irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core); /* Firmware resources management */ diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 15755614509a..b8645a9760f5 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -321,6 +321,20 @@ static irqreturn_t hdac_bus_irq_thread(int irq, void *context) return IRQ_HANDLED; } +static irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + + return avs_dsp_op(adev, irq_handler); +} + +static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) +{ + struct avs_dev *adev = dev_id; + + return avs_dsp_op(adev, irq_thread); +} + static int avs_hdac_acquire_irq(struct avs_dev *adev) { struct hdac_bus *bus = &adev->base.core; diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 29c7f508a7d6..ad0e535b3c2e 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -301,9 +301,8 @@ void avs_dsp_process_response(struct avs_dev *adev, u64 header) complete(&ipc->busy_completion); } -irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) +irqreturn_t avs_irq_handler(struct avs_dev *adev) { - struct avs_dev *adev = dev_id; struct avs_ipc *ipc = adev->ipc; const struct avs_spec *const spec = adev->spec; u32 adspis, hipc_rsp, hipc_ack; @@ -350,33 +349,6 @@ irqreturn_t avs_dsp_irq_handler(int irq, void *dev_id) return ret; } -irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) -{ - struct avs_dev *adev = dev_id; - union avs_reply_msg msg; - u32 hipct, hipcte; - - hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); - hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); - - /* ensure DSP sent new response to process */ - if (!(hipct & SKL_ADSP_HIPCT_BUSY)) - return IRQ_NONE; - - msg.primary = hipct; - msg.ext.val = hipcte; - avs_dsp_process_response(adev, msg.val); - - /* tell DSP we accepted its message */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, - SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); - /* unmask busy interrupt */ - snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, - AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); - - return IRQ_HANDLED; -} - static bool avs_ipc_is_busy(struct avs_ipc *ipc) { struct avs_dev *adev = to_avs_dev(ipc->dev); diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index 7ea8d91b54d2..d19f8953993f 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -12,6 +12,31 @@ #include "avs.h" #include "messages.h" +irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) +{ + union avs_reply_msg msg; + u32 hipct, hipcte; + + hipct = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); + hipcte = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); + + /* Ensure DSP sent new response to process. */ + if (!(hipct & SKL_ADSP_HIPCT_BUSY)) + return IRQ_NONE; + + msg.primary = hipct; + msg.ext.val = hipcte; + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, SKL_ADSP_HIPCT_BUSY); + /* Unmask busy interrupt. */ + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCCTL, AVS_ADSP_HIPCCTL_BUSY, + AVS_ADSP_HIPCCTL_BUSY); + + return IRQ_HANDLED; +} + static int __maybe_unused avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) @@ -103,8 +128,8 @@ const struct avs_dsp_ops avs_skl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, .stall = avs_dsp_core_stall, - .irq_handler = avs_dsp_irq_handler, - .irq_thread = avs_dsp_irq_thread, + .irq_handler = avs_irq_handler, + .irq_thread = avs_skl_irq_thread, .int_control = avs_dsp_interrupt_control, .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, -- cgit v1.2.3 From 8a6502ade116bc4b8293f094f8d74059c67c3f27 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:31 +0100 Subject: ASoC: Intel: avs: CNL-based platforms support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define handlers specific to cAVS 1.8 platforms, that is CNL, CFL, CML and all other variants based on this very version of AudioDSP architecture. Most operations are inherited from their predecessors. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/apl.c | 15 +++++----- sound/soc/intel/avs/avs.h | 8 ++++++ sound/soc/intel/avs/cnl.c | 61 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/core.c | 26 ++++++++++++++++++ sound/soc/intel/avs/registers.h | 15 ++++++++++ 6 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 sound/soc/intel/avs/cnl.c (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 460ee6599daf..a8d4b0ef2603 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -3,7 +3,7 @@ snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ topology.o path.o pcm.o board_selection.o control.o snd-soc-avs-objs += cldma.o -snd-soc-avs-objs += skl.o apl.o +snd-soc-avs-objs += skl.o apl.o cnl.o snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 6382543a9cdb..c21ecaef9eba 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -13,9 +13,9 @@ #include "path.h" #include "topology.h" -static int __maybe_unused -avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +#ifdef CONFIG_DEBUG_FS +int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { struct avs_apl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; @@ -47,8 +47,9 @@ avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_ return 0; } +#endif -static int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; @@ -102,7 +103,7 @@ static int avs_apl_wait_log_entry(struct avs_dev *adev, u32 core, /* reads log header and tests its type */ #define avs_apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) -static int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) { struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; @@ -202,7 +203,7 @@ static bool avs_apl_lp_streaming(struct avs_dev *adev) return true; } -static bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) { /* wake in all cases */ if (wake) @@ -219,7 +220,7 @@ static bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bo return avs_apl_lp_streaming(adev); } -static int avs_apl_set_d0ix(struct avs_dev *adev, bool enable) +int avs_apl_set_d0ix(struct avs_dev *adev, bool enable) { bool streaming = false; int ret; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 3d823880f965..b53cf35fa556 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -66,6 +66,7 @@ struct avs_dsp_ops { extern const struct avs_dsp_ops avs_skl_dsp_ops; extern const struct avs_dsp_ops avs_apl_dsp_ops; +extern const struct avs_dsp_ops avs_cnl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) @@ -265,7 +266,14 @@ int avs_dsp_disable_d0ix(struct avs_dev *adev); int avs_dsp_enable_d0ix(struct avs_dev *adev); irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); +irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev); +int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core); +int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg); +int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg); +bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake); +int avs_apl_set_d0ix(struct avs_dev *adev, bool enable); /* Firmware resources management */ diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c new file mode 100644 index 000000000000..5423c29ecc4e --- /dev/null +++ b/sound/soc/intel/avs/cnl.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include "avs.h" +#include "messages.h" + +irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev) +{ + union avs_reply_msg msg; + u32 hipctdr, hipctdd, hipctda; + + hipctdr = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR); + hipctdd = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD); + + /* Ensure DSP sent new response to process. */ + if (!(hipctdr & CNL_ADSP_HIPCTDR_BUSY)) + return IRQ_NONE; + + msg.primary = hipctdr; + msg.ext.val = hipctdd; + avs_dsp_process_response(adev, msg.val); + + /* Tell DSP we accepted its message. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDR, + CNL_ADSP_HIPCTDR_BUSY, CNL_ADSP_HIPCTDR_BUSY); + /* Ack this response. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCTDA, + CNL_ADSP_HIPCTDA_DONE, CNL_ADSP_HIPCTDA_DONE); + /* HW might have been clock gated, give some time for change to propagate. */ + snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda, + !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000); + /* Unmask busy interrupt. */ + snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCCTL, + AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_BUSY); + + return IRQ_HANDLED; +} + +const struct avs_dsp_ops avs_cnl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_skl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_apl_d0ix_toggle, + .set_d0ix = avs_apl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(apl) +}; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index b8645a9760f5..2054c2034fe2 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -767,6 +767,17 @@ static const struct avs_hipc_spec skl_hipc_spec = { .ctl_offset = SKL_ADSP_REG_HIPCCTL, }; +static const struct avs_hipc_spec cnl_hipc_spec = { + .req_offset = CNL_ADSP_REG_HIPCIDR, + .req_ext_offset = CNL_ADSP_REG_HIPCIDD, + .req_busy_mask = CNL_ADSP_HIPCIDR_BUSY, + .ack_offset = CNL_ADSP_REG_HIPCIDA, + .ack_done_mask = CNL_ADSP_HIPCIDA_DONE, + .rsp_offset = CNL_ADSP_REG_HIPCTDR, + .rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY, + .ctl_offset = CNL_ADSP_REG_HIPCCTL, +}; + static const struct avs_spec skl_desc = { .name = "skl", .min_fw_version = { 9, 21, 0, 4732 }, @@ -787,6 +798,16 @@ static const struct avs_spec apl_desc = { .hipc = &skl_hipc_spec, }; +static const struct avs_spec cnl_desc = { + .name = "cnl", + .min_fw_version = { 10, 23, 0, 5314 }, + .dsp_ops = &avs_cnl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, +}; + static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) }, @@ -796,6 +817,11 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, &cnl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 8468acd15c3d..6126adca500c 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -50,6 +50,21 @@ #define SKL_ADSP_HIPCIE_DONE BIT(30) #define SKL_ADSP_HIPCT_BUSY BIT(31) +/* CNL Intel HD Audio Inter-Processor Communication Registers */ +#define CNL_ADSP_IPC_BASE 0xC0 +#define CNL_ADSP_REG_HIPCTDR (CNL_ADSP_IPC_BASE + 0x00) +#define CNL_ADSP_REG_HIPCTDA (CNL_ADSP_IPC_BASE + 0x04) +#define CNL_ADSP_REG_HIPCTDD (CNL_ADSP_IPC_BASE + 0x08) +#define CNL_ADSP_REG_HIPCIDR (CNL_ADSP_IPC_BASE + 0x10) +#define CNL_ADSP_REG_HIPCIDA (CNL_ADSP_IPC_BASE + 0x14) +#define CNL_ADSP_REG_HIPCIDD (CNL_ADSP_IPC_BASE + 0x18) +#define CNL_ADSP_REG_HIPCCTL (CNL_ADSP_IPC_BASE + 0x28) + +#define CNL_ADSP_HIPCTDR_BUSY BIT(31) +#define CNL_ADSP_HIPCTDA_DONE BIT(31) +#define CNL_ADSP_HIPCIDR_BUSY BIT(31) +#define CNL_ADSP_HIPCIDA_DONE BIT(31) + /* Intel HD Audio SRAM windows base addresses */ #define SKL_ADSP_SRAM_BASE_OFFSET 0x8000 #define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000 -- cgit v1.2.3 From 275b583d047a23c48d01b0c45fb5d95618c1da2d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:32 +0100 Subject: ASoC: Intel: avs: ICL-based platforms support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define handlers specific to cAVS 2.0 platforms, that is ICL, JSL and all other variants based on this very version of AudioDSP architecture. Most operations are inherited from their predecessors with the major difference being firmware-logging functionality - IPC request as well as debug memory windows layout have changed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/avs.h | 6 ++ sound/soc/intel/avs/core.c | 24 ++++++++ sound/soc/intel/avs/icl.c | 137 +++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/avs/messages.c | 1 + sound/soc/intel/avs/messages.h | 28 +++++++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/icl.c (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index a8d4b0ef2603..6ababafd40bd 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -3,7 +3,7 @@ snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ topology.o path.o pcm.o board_selection.o control.o snd-soc-avs-objs += cldma.o -snd-soc-avs-objs += skl.o apl.o cnl.o +snd-soc-avs-objs += skl.o apl.o cnl.o icl.o snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index b53cf35fa556..27b2e1b18914 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -67,6 +67,7 @@ struct avs_dsp_ops { extern const struct avs_dsp_ops avs_skl_dsp_ops; extern const struct avs_dsp_ops avs_apl_dsp_ops; extern const struct avs_dsp_ops avs_cnl_dsp_ops; +extern const struct avs_dsp_ops avs_icl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) @@ -269,11 +270,16 @@ irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev); int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); +int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); int avs_skl_log_buffer_offset(struct avs_dev *adev, u32 core); +int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core); int avs_apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg); int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg); bool avs_apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake); +bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake); int avs_apl_set_d0ix(struct avs_dev *adev, bool enable); +int avs_icl_set_d0ix(struct avs_dev *adev, bool enable); /* Firmware resources management */ diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 2054c2034fe2..17444ffca019 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -808,6 +808,26 @@ static const struct avs_spec cnl_desc = { .hipc = &cnl_hipc_spec, }; +static const struct avs_spec icl_desc = { + .name = "icl", + .min_fw_version = { 10, 23, 0, 5040 }, + .dsp_ops = &avs_icl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, +}; + +static const struct avs_spec jsl_desc = { + .name = "jsl", + .min_fw_version = { 10, 26, 0, 5872 }, + .dsp_ops = &avs_icl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_IMR, + .sram = &apl_sram_spec, + .hipc = &cnl_hipc_spec, +}; + static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) }, @@ -822,6 +842,10 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cnl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cnl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c new file mode 100644 index 000000000000..83ebee6f87ac --- /dev/null +++ b/sound/soc/intel/avs/icl.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include "avs.h" +#include "messages.h" + +#ifdef CONFIG_DEBUG_FS +int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +{ + struct avs_icl_log_state_info *info; + u32 size, num_libs = adev->fw_cfg.max_libs_count; + int i, ret; + + if (fls_long(resource_mask) > num_libs) + return -EINVAL; + size = struct_size(info, logs_priorities_mask, num_libs); + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->aging_timer_period = aging_period; + info->fifo_full_timer_period = fifo_full_period; + info->enable = enable; + if (enable) + for_each_set_bit(i, &resource_mask, num_libs) + info->logs_priorities_mask[i] = *priorities++; + + ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size); + kfree(info); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} +#endif + +union avs_icl_memwnd2_slot_type { + u32 val; + struct { + u32 resource_id:8; + u32 type:24; + }; +} __packed; + +struct avs_icl_memwnd2_desc { + u32 resource_id; + union avs_icl_memwnd2_slot_type slot_id; + u32 vma; +} __packed; + +#define AVS_ICL_MEMWND2_SLOTS_COUNT 15 + +struct avs_icl_memwnd2 { + union { + struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; + u8 rsvd[PAGE_SIZE]; + }; + u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; +} __packed; + +#define AVS_ICL_SLOT_UNUSED \ + ((union avs_icl_memwnd2_slot_type) { 0x00000000U }) +#define AVS_ICL_SLOT_CRITICAL_LOG \ + ((union avs_icl_memwnd2_slot_type) { 0x54524300U }) +#define AVS_ICL_SLOT_DEBUG_LOG \ + ((union avs_icl_memwnd2_slot_type) { 0x474f4c00U }) +#define AVS_ICL_SLOT_GDB_STUB \ + ((union avs_icl_memwnd2_slot_type) { 0x42444700U }) +#define AVS_ICL_SLOT_BROKEN \ + ((union avs_icl_memwnd2_slot_type) { 0x44414544U }) + +static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_type slot_type) +{ + struct avs_icl_memwnd2_desc desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; + int i; + + memcpy_fromio(&desc, avs_sram_addr(adev, AVS_DEBUG_WINDOW), sizeof(desc)); + + for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++) + if (desc[i].slot_id.val == slot_type.val) + return offsetof(struct avs_icl_memwnd2, slot_array) + + avs_skl_log_buffer_offset(adev, i); + return -ENXIO; +} + +int avs_icl_log_buffer_offset(struct avs_dev *adev, u32 core) +{ + union avs_icl_memwnd2_slot_type slot_type = AVS_ICL_SLOT_DEBUG_LOG; + int ret; + + slot_type.resource_id = core; + ret = avs_icl_slot_offset(adev, slot_type); + if (ret < 0) + dev_dbg(adev->dev, "No slot offset found for: %x\n", + slot_type.val); + + return ret; +} + +bool avs_icl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +{ + /* Payload-less IPCs do not take part in d0ix toggling. */ + return tx->size; +} + +int avs_icl_set_d0ix(struct avs_dev *adev, bool enable) +{ + int ret; + + ret = avs_ipc_set_d0ix(adev, enable, false); + return AVS_IPC_RET(ret); +} + +const struct avs_dsp_ops avs_icl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_icl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_icl_d0ix_toggle, + .set_d0ix = avs_icl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(icl) +}; diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 06b4394cabd2..f874e4f0d95f 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -381,6 +381,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) msg.ext.set_d0ix.wake = enable_pg; msg.ext.set_d0ix.streaming = streaming; + msg.ext.set_d0ix.prevent_pg = !enable_pg; request.header = msg.val; diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 0f0862818f02..4e609a08863c 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -145,8 +145,12 @@ union avs_module_msg { u32 src_queue:3; } bind_unbind; struct { + /* pre-IceLake */ u32 wake:1; u32 streaming:1; + /* IceLake and onwards */ + u32 prevent_pg:1; + u32 prevent_local_cg:1; } set_d0ix; } ext; }; @@ -376,6 +380,30 @@ struct avs_apl_log_state_info { struct avs_skl_log_state logs_core[]; } __packed; +enum avs_icl_log_priority { + AVS_ICL_LOG_CRITICAL = 0, + AVS_ICL_LOG_HIGH, + AVS_ICL_LOG_MEDIUM, + AVS_ICL_LOG_LOW, + AVS_ICL_LOG_VERBOSE, +}; + +enum avs_icl_log_source { + AVS_ICL_LOG_INFRA = 0, + AVS_ICL_LOG_HAL, + AVS_ICL_LOG_MODULE, + AVS_ICL_LOG_AUDIO, + AVS_ICL_LOG_SENSING, + AVS_ICL_LOG_ULP_INFRA, +}; + +struct avs_icl_log_state_info { + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 enable; + u32 logs_priorities_mask[]; +} __packed; + int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size); struct avs_fw_version { -- cgit v1.2.3 From 5acb19ecd1982bd1578912473b33df75a23fefc2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:33 +0100 Subject: ASoC: Intel: avs: TGL-based platforms support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define handlers specific to cAVS 2.5 platforms, that is TGL, ADL, RPL and all other variants based on this very version of AudioDSP architecture. Most operations are inherited from their predecessors with the major difference being AudioDSP cores management - firmware handlers that on its own so there is no need to interfere. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/Makefile | 2 +- sound/soc/intel/avs/avs.h | 1 + sound/soc/intel/avs/core.c | 34 ++++++++++++++++++++++++++++ sound/soc/intel/avs/tgl.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/avs/tgl.c (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 6ababafd40bd..382bd00ccf4c 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -3,7 +3,7 @@ snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ topology.o path.o pcm.o board_selection.o control.o snd-soc-avs-objs += cldma.o -snd-soc-avs-objs += skl.o apl.o cnl.o icl.o +snd-soc-avs-objs += skl.o apl.o cnl.o icl.o tgl.o snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 27b2e1b18914..703bb1f145fa 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -68,6 +68,7 @@ extern const struct avs_dsp_ops avs_skl_dsp_ops; extern const struct avs_dsp_ops avs_apl_dsp_ops; extern const struct avs_dsp_ops avs_cnl_dsp_ops; extern const struct avs_dsp_ops avs_icl_dsp_ops; +extern const struct avs_dsp_ops avs_tgl_dsp_ops; #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 17444ffca019..adc2b9300734 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -828,6 +828,23 @@ static const struct avs_spec jsl_desc = { .hipc = &cnl_hipc_spec, }; +#define AVS_TGL_BASED_SPEC(sname) \ +static const struct avs_spec sname##_desc = { \ + .name = #sname, \ + .min_fw_version = { 10, 29, 0, 5646 }, \ + .dsp_ops = &avs_tgl_dsp_ops, \ + .core_init_mask = 1, \ + .attributes = AVS_PLATATTR_IMR, \ + .sram = &apl_sram_spec, \ + .hipc = &cnl_hipc_spec, \ +} + +AVS_TGL_BASED_SPEC(lkf); +AVS_TGL_BASED_SPEC(tgl); +AVS_TGL_BASED_SPEC(ehl); +AVS_TGL_BASED_SPEC(adl); +AVS_TGL_BASED_SPEC(adl_n); + static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) }, @@ -846,6 +863,23 @@ static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &icl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) }, { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_LKF, &lkf_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_R, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &adl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c new file mode 100644 index 000000000000..8abdff4fbb87 --- /dev/null +++ b/sound/soc/intel/avs/tgl.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2024 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include "avs.h" + +static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_power(adev, core_mask, power); +} + +static int avs_tgl_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_reset(adev, core_mask, reset); +} + +static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) +{ + core_mask &= AVS_MAIN_CORE_MASK; + + if (!core_mask) + return 0; + return avs_dsp_core_stall(adev, core_mask, stall); +} + +const struct avs_dsp_ops avs_tgl_dsp_ops = { + .power = avs_tgl_dsp_core_power, + .reset = avs_tgl_dsp_core_reset, + .stall = avs_tgl_dsp_core_stall, + .irq_handler = avs_irq_handler, + .irq_thread = avs_cnl_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .log_buffer_offset = avs_icl_log_buffer_offset, + .log_buffer_status = avs_apl_log_buffer_status, + .coredump = avs_apl_coredump, + .d0ix_toggle = avs_icl_d0ix_toggle, + .set_d0ix = avs_icl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(icl) +}; -- cgit v1.2.3 From 36478a74c7ddaf58d80da5cef9c5ddb5beed5a2e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:34 +0100 Subject: ASoC: Intel: avs: ICCMAX recommendations for ICL+ platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For ICL+ platforms to avoid DMI/OPIO L1 entry during the base firmware load procedure, HW recommends to set LTRP_GB to 95us and start an additional CAPTURE stream in the background. Once the load completes, original LTRP_GB value is restored and the additional stream is released. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-10-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/hda_register.h | 2 ++ sound/soc/intel/avs/avs.h | 2 ++ sound/soc/intel/avs/icl.c | 62 +++++++++++++++++++++++++++++++++++++++++++- sound/soc/intel/avs/tgl.c | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h index 55958711d697..5ff31e6d41c1 100644 --- a/include/sound/hda_register.h +++ b/include/sound/hda_register.h @@ -131,6 +131,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20 +#define AZX_REG_VS_LTRP_GB_MASK GENMASK(6, 0) + /* PCI space */ #define AZX_PCIREG_TCSEL 0x44 diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 703bb1f145fa..a58c1500f612 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -325,6 +325,8 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); int avs_hda_transfer_modules(struct avs_dev *adev, bool load, struct avs_module_entry *mods, u32 num_mods); +int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw); + /* Soc component members */ struct avs_soc_component { diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c index 83ebee6f87ac..9d9921e1cd4d 100644 --- a/sound/soc/intel/avs/icl.c +++ b/sound/soc/intel/avs/icl.c @@ -7,9 +7,13 @@ // #include +#include +#include #include "avs.h" #include "messages.h" +#define ICL_VS_LTRP_GB_ICCMAX 95 + #ifdef CONFIG_DEBUG_FS int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) @@ -118,6 +122,62 @@ int avs_icl_set_d0ix(struct avs_dev *adev, bool enable) return AVS_IPC_RET(ret); } +int avs_icl_load_basefw(struct avs_dev *adev, struct firmware *fw) +{ + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_stream *host_stream; + struct snd_pcm_substream substream; + struct snd_dma_buffer dmab; + unsigned int sd_fmt; + u8 ltrp_gb; + int ret; + + /* + * ICCMAX: + * + * For ICL+ platforms, as per HW recommendation LTRP_GB is set to 95us + * during FW load. Its original value shall be restored once load completes. + * + * To avoid DMI/OPIO L1 entry during the load procedure, additional CAPTURE + * stream is allocated and set to run. + */ + + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + + host_stream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); + if (!host_stream) + return -EBUSY; + + ltrp_gb = snd_hdac_chip_readb(bus, VS_LTRP) & AZX_REG_VS_LTRP_GB_MASK; + /* Carries no real data, use default format. */ + sd_fmt = snd_hdac_stream_format(1, 32, 48000); + + ret = snd_hdac_dsp_prepare(hdac_stream(host_stream), sd_fmt, fw->size, &dmab); + if (ret < 0) + goto release_stream; + + snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ICL_VS_LTRP_GB_ICCMAX); + + spin_lock(&bus->reg_lock); + snd_hdac_stream_start(hdac_stream(host_stream)); + spin_unlock(&bus->reg_lock); + + ret = avs_hda_load_basefw(adev, fw); + + spin_lock(&bus->reg_lock); + snd_hdac_stream_stop(hdac_stream(host_stream)); + spin_unlock(&bus->reg_lock); + + snd_hdac_dsp_cleanup(hdac_stream(host_stream), &dmab); + +release_stream: + snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST); + snd_hdac_chip_updateb(bus, VS_LTRP, AZX_REG_VS_LTRP_GB_MASK, ltrp_gb); + + return ret; +} + const struct avs_dsp_ops avs_icl_dsp_ops = { .power = avs_dsp_core_power, .reset = avs_dsp_core_reset, @@ -125,7 +185,7 @@ const struct avs_dsp_ops avs_icl_dsp_ops = { .irq_handler = avs_irq_handler, .irq_thread = avs_cnl_irq_thread, .int_control = avs_dsp_interrupt_control, - .load_basefw = avs_hda_load_basefw, + .load_basefw = avs_icl_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, .log_buffer_offset = avs_icl_log_buffer_offset, diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c index 8abdff4fbb87..0e052e7f6bac 100644 --- a/sound/soc/intel/avs/tgl.c +++ b/sound/soc/intel/avs/tgl.c @@ -42,7 +42,7 @@ const struct avs_dsp_ops avs_tgl_dsp_ops = { .irq_handler = avs_irq_handler, .irq_thread = avs_cnl_irq_thread, .int_control = avs_dsp_interrupt_control, - .load_basefw = avs_hda_load_basefw, + .load_basefw = avs_icl_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, .log_buffer_offset = avs_icl_log_buffer_offset, -- cgit v1.2.3 From 5b417fe0cded0b5917683398e6519aae8045cd40 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 20 Feb 2024 12:50:35 +0100 Subject: ASoC: Intel: avs: Populate board selection with new I2S entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update board selection with tables specifying supported I2S configurations. DMIC/HDAudio board selection require no update as dmic/hdaudio machine boards are generic and not tied to any specific codec. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240220115035.770402-11-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/board_selection.c | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 8e91eece992d..8360ce557401 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -236,6 +236,82 @@ static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = { {}, }; +static struct snd_soc_acpi_mach avs_cnl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC5682", + .drv_name = "avs_rt5682", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5682-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_icl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { + { + .id = "INT34C2", + .drv_name = "avs_rt274", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt274-tplg.bin", + }, + { + .id = "10EC0298", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "10EC1308", + .drv_name = "avs_rt1308", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt1308-tplg.bin", + }, + { + .id = "ESSX8336", + .drv_name = "avs_es8336", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "es8336-tplg.bin", + }, + {}, +}; + static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { { .drv_name = "avs_i2s_test", @@ -296,6 +372,15 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines), AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines), AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CNL_H, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_CML_LP, avs_cnl_i2s_machines), + AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), + AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), {}, }; -- cgit v1.2.3 From 0dae534c48239be0a99092e46e1baade0cf3e04a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 29 Jan 2024 12:52:16 +0100 Subject: ASoC: codecs: wsa884x: Allow sharing reset GPIO On some boards with multiple WSA8840/WSA8845 speakers, the reset (shutdown) GPIO is shared between two speakers. Use the reset controller framework and its "reset-gpio" driver to handle this case. This allows bring-up and proper handling of all WSA884x speakers on X1E80100-CRD board. Cc: Bartosz Golaszewski Cc: Sean Anderson Reviewed-by: Philipp Zabel Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240129115216.96479-7-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa884x.c | 53 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index f2653df84e4a..a9767ef0e39d 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -699,6 +700,7 @@ struct wsa884x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; + struct reset_control *sd_reset; bool port_prepared[WSA884X_MAX_SWR_PORTS]; bool port_enable[WSA884X_MAX_SWR_PORTS]; unsigned int variant; @@ -1799,9 +1801,22 @@ static struct snd_soc_dai_driver wsa884x_dais[] = { }, }; -static void wsa884x_gpio_powerdown(void *data) +static void wsa884x_reset_powerdown(void *data) { - gpiod_direction_output(data, 1); + struct wsa884x_priv *wsa884x = data; + + if (wsa884x->sd_reset) + reset_control_assert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 1); +} + +static void wsa884x_reset_deassert(struct wsa884x_priv *wsa884x) +{ + if (wsa884x->sd_reset) + reset_control_deassert(wsa884x->sd_reset); + else + gpiod_direction_output(wsa884x->sd_n, 0); } static void wsa884x_regulator_disable(void *data) @@ -1809,6 +1824,27 @@ static void wsa884x_regulator_disable(void *data) regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); } +static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x) +{ + wsa884x->sd_reset = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(wsa884x->sd_reset)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_reset), + "Failed to get reset\n"); + else if (wsa884x->sd_reset) + return 0; + /* + * else: NULL, so use the backwards compatible way for powerdown-gpios, + * which does not handle sharing GPIO properly. + */ + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + return 0; +} + static int wsa884x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1838,11 +1874,9 @@ static int wsa884x_probe(struct sdw_slave *pdev, if (ret) return ret; - wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", - GPIOD_OUT_HIGH); - if (IS_ERR(wsa884x->sd_n)) - return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), - "Shutdown Control GPIO not found\n"); + ret = wsa884x_get_reset(dev, wsa884x); + if (ret) + return ret; dev_set_drvdata(dev, wsa884x); wsa884x->slave = pdev; @@ -1858,9 +1892,8 @@ static int wsa884x_probe(struct sdw_slave *pdev, pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; - /* Bring out of reset */ - gpiod_direction_output(wsa884x->sd_n, 0); - ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + wsa884x_reset_deassert(wsa884x); + ret = devm_add_action_or_reset(dev, wsa884x_reset_powerdown, wsa884x); if (ret) return ret; -- cgit v1.2.3 From e2cb72d28740516cb03fa072e14b2f1a6eceef61 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:11 +0100 Subject: ASoC: codecs: da7213: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7213.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 0e5c527687a2..369c62078780 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2101,18 +2101,14 @@ static int da7213_probe(struct snd_soc_component *component) pm_runtime_put_sync(component->dev); /* Check if MCLK provided */ - da7213->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(da7213->mclk)) { - if (PTR_ERR(da7213->mclk) != -ENOENT) - return PTR_ERR(da7213->mclk); - else - da7213->mclk = NULL; - } else { + da7213->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(da7213->mclk)) + return PTR_ERR(da7213->mclk); + if (da7213->mclk) /* Do automatic PLL handling assuming fixed clock until * set_pll() has been called. This makes the codec usable * with the simple-audio-card driver. */ da7213->fixed_clk_auto_pll = true; - } /* Default infinite tone gen, start/stop by Kcontrol */ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK); -- cgit v1.2.3 From 71d322fd16a3a62d32a9e6a8d08f48e8a945a515 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:12 +0100 Subject: ASoC: codecs: nau8825: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 5cb0de648bd3..cd30ad649bae 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2836,16 +2836,12 @@ static int nau8825_read_device_properties(struct device *dev, if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500) dev_warn(dev, "Please set the suitable delay time!\n"); - nau8825->mclk = devm_clk_get(dev, "mclk"); - if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + nau8825->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(nau8825->mclk)) + return PTR_ERR(nau8825->mclk); + if (!nau8825->mclk) /* The MCLK is managed externally or not used at all */ - nau8825->mclk = NULL; dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); - } else if (IS_ERR(nau8825->mclk)) { - return -EINVAL; - } return 0; } -- cgit v1.2.3 From 67e9bf093372a070f67f85a6ffceb6a44d4cfcf4 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:13 +0100 Subject: ASoC: codecs: rt5514: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5514.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 43fc7814fdde..a8cdc3d6994d 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1054,9 +1054,6 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_PREPARE: - if (IS_ERR(rt5514->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5514->mclk); } else { @@ -1097,9 +1094,9 @@ static int rt5514_probe(struct snd_soc_component *component) struct platform_device *pdev = container_of(component->dev, struct platform_device, dev); - rt5514->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5514->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5514->mclk)) + return PTR_ERR(rt5514->mclk); if (rt5514->pdata.dsp_calib_clk_name) { rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev, -- cgit v1.2.3 From f76de61ad1eb725cc05727377ccd4adda336b822 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:14 +0100 Subject: ASoC: codecs: rt5616: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5616.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index c13108b51eaf..e7aa60e73961 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1174,9 +1174,6 @@ static int rt5616_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5616->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5616->mclk); } else { @@ -1225,9 +1222,9 @@ static int rt5616_probe(struct snd_soc_component *component) struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); /* Check if MCLK provided */ - rt5616->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5616->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5616->mclk)) + return PTR_ERR(rt5616->mclk); rt5616->component = component; -- cgit v1.2.3 From 6413849b678b04e30b5c938e344e653c31a5f73b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:15 +0100 Subject: ASoC: codecs: rt5640: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. rt5640_set_dai_sysclk() is an example of that - clk_set_rate() is not guarded by IS_ERR(). By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5640.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index e8cdc166bdaa..174872ef35d2 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1949,9 +1949,6 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (IS_ERR(rt5640->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5640->mclk); } else { @@ -2661,9 +2658,9 @@ static int rt5640_probe(struct snd_soc_component *component) u32 val; /* Check if MCLK provided */ - rt5640->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5640->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5640->mclk)) + return PTR_ERR(rt5640->mclk); rt5640->component = component; -- cgit v1.2.3 From bf900c85f8a4ef47b868b6345879e35826a4fec1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 21 Feb 2024 16:25:16 +0100 Subject: ASoC: codecs: rt5660: Simplify mclk initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of clk_xxx() functions do check if provided clk-pointer is non-NULL. These do not check if the pointer is an error-pointer. Providing such to a clk_xxx() results in a panic. By utilizing _optional() variant of devm_clk_get() the driver code is both simplified and more robust. There is no need to remember about IS_ERR(clk) checks each time mclk is accessed. Reviewed-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240221152516.852353-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5660.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 0cecfd602415..d5c2f0f2df98 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1079,9 +1079,6 @@ static int rt5660_set_bias_level(struct snd_soc_component *component, snd_soc_component_update_bits(component, RT5660_GEN_CTRL1, RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL); - if (IS_ERR(rt5660->mclk)) - break; - if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { clk_disable_unprepare(rt5660->mclk); } else { @@ -1277,9 +1274,9 @@ static int rt5660_i2c_probe(struct i2c_client *i2c) return -ENOMEM; /* Check if MCLK provided */ - rt5660->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + rt5660->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5660->mclk)) + return PTR_ERR(rt5660->mclk); i2c_set_clientdata(i2c, rt5660); -- cgit v1.2.3 From 4c75493833a6e2095f03639f66aed5fbf2683c73 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 22 Feb 2024 15:56:55 +0530 Subject: ASoC: amd: ps: update license To align with AMD SoundWire manager driver license, update license as GPL-2.0-only for Pink Sardine ACP PCI driver and corresponding child drivers. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240222102656.631144-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/Makefile | 2 +- sound/soc/amd/ps/pci-ps.c | 2 +- sound/soc/amd/ps/ps-mach.c | 2 +- sound/soc/amd/ps/ps-pdm-dma.c | 2 +- sound/soc/amd/ps/ps-sdw-dma.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile index f2a5eaf2fa4d..b3c254886fd9 100644 --- a/sound/soc/amd/ps/Makefile +++ b/sound/soc/amd/ps/Makefile @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0+ +# SPDX-License-Identifier: GPL-2.0-only # Pink Sardine platform Support snd-pci-ps-objs := pci-ps.o snd-ps-pdm-dma-objs := ps-pdm-dma.o diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 205bca95aa06..c72d666d51bd 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD Pink Sardine ACP PCI Driver * diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c index 3ffbe4fdafdf..e675b8f569eb 100644 --- a/sound/soc/amd/ps/ps-mach.c +++ b/sound/soc/amd/ps/ps-mach.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * Machine driver for AMD Pink Sardine platform using DMIC * diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index d48f7c5af289..7bbacbab1095 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD ALSA SoC Pink Sardine PDM Driver * diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 9b59063798f2..66b800962f8c 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-only /* * AMD ALSA SoC Pink Sardine SoundWire DMA Driver * -- cgit v1.2.3 From 253ce07d2a091e98ef53e700e7fa221b28c4f964 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 22 Feb 2024 15:56:56 +0530 Subject: ASoC: amd: ps: modify ACP register end address macro Modify ACP63_REG_END macro to access all ACP registers. Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240222102656.631144-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/ps/acp63.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 65433184d03e..39208305dd6c 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -10,7 +10,7 @@ #define ACP_DEVICE_ID 0x15E2 #define ACP63_REG_START 0x1240000 -#define ACP63_REG_END 0x1250200 +#define ACP63_REG_END 0x125C000 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 #define ACP_PGFSM_CNTL_POWER_ON_MASK 1 -- cgit v1.2.3 From b1724c00f0d9224c50a4fab6a85be8e2155a9a1b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Feb 2024 05:51:11 +0000 Subject: ASoC: soc-core: tidyup strcmp() param on snd_soc_is_matching_dai() snd_soc_is_matching_dai() checks DAI name, which is paired function with snd_soc_dai_name_get(). It checks dlc->dai_name and dai->name (A) or dai->driver_name (B) or dai->component->name (C) static int snd_soc_is_matching_dai(...) { ... if (strcmp(dlc->dai_name, dai->name) == 0) ~~~~~~~~~~~~~ ^^^^^^^^^(A) if (... strcmp(dai->driver->name, dlc->dai_name) == 0) (B)^^^^^^^^^^^^^^^^ ~~~~~~~~~~~~~ if (... strcmp(dlc->dai_name, dai->component->name) == 0) ~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^^(C) ... } But (B) part order is different with (A) and (C) (= ^^^^ and ~~~~). This is not a big deal, but confusable to read. Fixup it. Signed-off-by: Kuninori Morimoto Link: https://msgid.link/r/87wmqxjbcg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b11b2ca5d939..507cd3015ff4 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -287,7 +287,7 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, return 1; if (dai->driver->name && - strcmp(dai->driver->name, dlc->dai_name) == 0) + strcmp(dlc->dai_name, dai->driver->name) == 0) return 1; if (dai->component->name && -- cgit v1.2.3 From 5519ac3a7164d5d1c31879bf5b0d279b58c8e88f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 Feb 2024 15:39:05 +0000 Subject: ASoC: wm_adsp: Add wm_adsp_start() and wm_adsp_stop() Separate the functionality of wm_adsp_event() into two exported functions wm_adsp_start() and wm_adsp_stop(). This allows the codec driver to start and stop the DSP outside of a DAPM widget. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240223153910.2063698-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 27 ++++++++++++++++++--------- sound/soc/codecs/wm_adsp.h | 2 ++ 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 36ea0dcdc7ab..e451c009f2d9 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1092,27 +1092,36 @@ static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) dsp->fatal_error = false; } +int wm_adsp_run(struct wm_adsp *dsp) +{ + flush_work(&dsp->boot_work); + + return cs_dsp_run(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_run); + +void wm_adsp_stop(struct wm_adsp *dsp) +{ + cs_dsp_stop(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_stop); + int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - int ret = 0; switch (event) { case SND_SOC_DAPM_POST_PMU: - flush_work(&dsp->boot_work); - ret = cs_dsp_run(&dsp->cs_dsp); - break; + return wm_adsp_run(dsp); case SND_SOC_DAPM_PRE_PMD: - cs_dsp_stop(&dsp->cs_dsp); - break; + wm_adsp_stop(dsp); + return 0; default: - break; + return 0; } - - return ret; } EXPORT_SYMBOL_GPL(wm_adsp_event); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 067d807a7ca8..e53dfcf1f78f 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -98,6 +98,8 @@ irqreturn_t wm_adsp2_bus_error(int irq, void *data); irqreturn_t wm_halo_bus_error(int irq, void *data); irqreturn_t wm_halo_wdt_expire(int irq, void *data); +int wm_adsp_run(struct wm_adsp *dsp); +void wm_adsp_stop(struct wm_adsp *dsp); int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -- cgit v1.2.3 From 1cad8725f2b98965ed3658bc917090b30adb14fa Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 Feb 2024 15:39:06 +0000 Subject: ASoC: cs-amp-lib: Add helpers for factory calibration data Create a new library for code that is used by multiple Cirrus Logic amps. This initially implements extracting amp calibration data from EFI and writing it to firmware controls. During factory calibration of built-in speakers the firmware calibration constants are stored in an EFI file. The file contains an array of calibration constants for each of the speakers. cs_amp_get_calibration_data() searches for an entry matching the requested UID stamp, otherwise by array index. If the data is found in EFI the constants for that speaker are copied back to the caller. If EFI is not enabled, the cs_amp_get_calibration_data() implementation will compile to simply return -ENOENT and the linker can drop the code. The code to write calibration controls uses cs_dsp. Building of cs_dsp is not forced. Instead, the code will compile away the calls to cs_dsp if cs_dsp is not reachable. This strategy of conditional code allows cs-amp-lib to be shared by multiple drivers without forcing inclusion of other modules that might be unnecessary. The calls to efi.get_variable() and cs_dsp are in small wrapper functions. This is so that a KUNIT_STATIC_STUB_REDIRECT can be added in a future patch to redirect these calls to replacement functions for KUnit testing. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240223153910.2063698-3-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs-amp-lib.h | 52 +++++++++ sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs-amp-lib.c | 263 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+) create mode 100644 include/sound/cs-amp-lib.h create mode 100644 sound/soc/codecs/cs-amp-lib.c (limited to 'sound/soc') diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h new file mode 100644 index 000000000000..077fe36885b5 --- /dev/null +++ b/include/sound/cs-amp-lib.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2024 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS_AMP_LIB_H +#define CS_AMP_LIB_H + +#include +#include + +struct cs_dsp; + +struct cirrus_amp_cal_data { + u32 calTarget[2]; + u32 calTime[2]; + s8 calAmbient; + u8 calStatus; + u16 calR; +} __packed; + +struct cirrus_amp_efi_data { + u32 size; + u32 count; + struct cirrus_amp_cal_data data[]; +} __packed; + +/** + * struct cirrus_amp_cal_controls - definition of firmware calibration controls + * @alg_id: ID of algorithm containing the controls. + * @mem_region: DSP memory region containing the controls. + * @ambient: Name of control for calAmbient value. + * @calr: Name of control for calR value. + * @status: Name of control for calStatus value. + * @checksum: Name of control for checksum value. + */ +struct cirrus_amp_cal_controls { + unsigned int alg_id; + int mem_region; + const char *ambient; + const char *calr; + const char *status; + const char *checksum; +}; + +int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data); +int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data); +#endif /* CS_AMP_LIB_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 59f9742e9ff4..109848a7a413 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -727,6 +727,9 @@ config SND_SOC_CROS_EC_CODEC If you say yes here you will get support for the ChromeOS Embedded Controller's Audio Codec. +config SND_SOC_CS_AMP_LIB + tristate + config SND_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f53baa2b9565..0caa3209673b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -59,6 +59,7 @@ snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o +snd-soc-cs-amp-lib-objs := cs-amp-lib.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o @@ -449,6 +450,7 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c new file mode 100644 index 000000000000..4e2e5157a73f --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Common code for Cirrus Logic Smart Amplifiers +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS_AMP_CAL_GUID \ + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) + +#define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibrationData" + +static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct cs_dsp_coeff_ctl *cs_ctl; + __be32 beval = cpu_to_be32(val); + int ret; + + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { + mutex_lock(&dsp->pwr_lock); + cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); + mutex_unlock(&dsp->pwr_lock); + + if (ret < 0) { + dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); + return ret; + } + + return 0; + } + + return -ENODEV; +} + +static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + int ret; + + dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", + data->calAmbient, data->calStatus, data->calR); + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1); + if (ret) + return ret; + + return 0; +} + +/** + * cs_amp_write_cal_coeffs - Write calibration data to firmware controls. + * @dsp: Pointer to struct cs_dsp. + * @controls: Pointer to definition of firmware controls to be written. + * @data: Pointer to calibration data. + * + * Returns: 0 on success, else negative error value. + */ +int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const struct cirrus_amp_cal_data *data) +{ + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) + return _cs_amp_write_cal_coeffs(dsp, controls, data); + else + return -ENODEV; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, SND_SOC_CS_AMP_LIB); + +static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + u32 attr; + + if (IS_ENABLED(CONFIG_EFI)) + return efi.get_variable(name, guid, &attr, size, buf); + + return EFI_NOT_FOUND; +} + +static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) +{ + struct cirrus_amp_efi_data *efi_data; + unsigned long data_size = 0; + u8 *data; + efi_status_t status; + int ret; + + /* Get real size of UEFI variable */ + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return ERR_PTR(-ENOENT); + + if (data_size < sizeof(*efi_data)) { + dev_err(dev, "EFI cal variable truncated\n"); + return ERR_PTR(-EOVERFLOW); + } + + /* Get variable contents into buffer */ + data = kmalloc(data_size, GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data); + if (status != EFI_SUCCESS) { + ret = -EINVAL; + goto err; + } + + efi_data = (struct cirrus_amp_efi_data *)data; + dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); + + if ((efi_data->count > 128) || + offsetof(struct cirrus_amp_efi_data, data[efi_data->count]) > data_size) { + dev_err(dev, "EFI cal variable truncated\n"); + ret = -EOVERFLOW; + goto err; + } + + return efi_data; + +err: + kfree(data); + dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); + + return ERR_PTR(ret); +} + +static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data) +{ + return ((u64)data->calTarget[1] << 32) | data->calTarget[0]; +} + +static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + struct cirrus_amp_efi_data *efi_data; + struct cirrus_amp_cal_data *cal = NULL; + int i, ret; + + efi_data = cs_amp_get_cal_efi_buffer(dev); + if (IS_ERR(efi_data)) + return PTR_ERR(efi_data); + + if (target_uid) { + for (i = 0; i < efi_data->count; ++i) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]); + + /* Skip entries with unpopulated silicon ID */ + if (cal_target == 0) + continue; + + if (cal_target == target_uid) { + cal = &efi_data->data[i]; + break; + } + } + } + + if (!cal && (amp_index >= 0) && (amp_index < efi_data->count)) { + u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]); + + /* + * Treat unpopulated cal_target as a wildcard. + * If target_uid != 0 we can only get here if cal_target == 0 + * or it didn't match any cal_target value. + * If target_uid == 0 it is a wildcard. + */ + if ((cal_target == 0) || (target_uid == 0)) + cal = &efi_data->data[amp_index]; + else + dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); + } + + if (cal) { + memcpy(out_data, cal, sizeof(*out_data)); + ret = 0; + } else { + dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); + ret = -ENOENT; + } + + kfree(efi_data); + + return ret; +} + +/** + * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI. + * @dev: struct device of the caller. + * @target_uid: UID to match, or zero to ignore UID matching. + * @amp_index: Entry index to use, or -1 to prevent lookup by index. + * @out_data: struct cirrus_amp_cal_data where the entry will be copied. + * + * This function can perform 3 types of lookup: + * + * (target_uid > 0, amp_index >= 0) + * UID search with fallback to using the array index. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else, if the entry at + * [amp_index] has calTarget == 0, return that entry. Else fail. + * + * (target_uid > 0, amp_index < 0) + * UID search only. + * Search the calibration data for a non-zero calTarget that matches + * target_uid, and if found return that entry. Else fail. + * + * (target_uid == 0, amp_index >= 0) + * Array index fetch only. + * Return the entry at [amp_index]. + * + * An array lookup will be skipped if amp_index exceeds the number of + * entries in the calibration array, and in this case the return will + * be -ENOENT. An out-of-range amp_index does not prevent matching by + * target_uid - it has the same effect as passing amp_index < 0. + * + * If the EFI data is too short to be a valid entry, or the entry count + * in the EFI data overflows the actual length of the data, this function + * returns -EOVERFLOW. + * + * Return: 0 if the entry was found, -ENOENT if no entry was found, + * -EOVERFLOW if the EFI file is corrupt, else other error value. + */ +int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, + struct cirrus_amp_cal_data *out_data) +{ + if (IS_ENABLED(CONFIG_EFI)) + return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); + else + return -ENOENT; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, SND_SOC_CS_AMP_LIB); + +MODULE_DESCRIPTION("Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(FW_CS_DSP); -- cgit v1.2.3 From e1830f66f6c62d288d2c27a7ed18ab93caa0b253 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 Feb 2024 15:39:07 +0000 Subject: ASoC: cs35l56: Add helper functions for amp calibration Adds some helper functions and data for applying amp calibration. 1. cs35l56_read_silicon_uid() to get the silicon ID that is used to search for the correct calibration data entry. 2. Add the registers for the silicon ID to the readable registers. 3. cs35l56_get_calibration() wrapper around cs_amp_get_efi_calibration_data() 4. cs35l56_calibration_controls() table of the firmware controls for calibration data. 5. Added members to struct cs35l56_base to store the calibration data. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240223153910.2063698-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 10 +++++ sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/cs35l56-shared.c | 83 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) (limited to 'sound/soc') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index b24716ab2750..4014ed7097b3 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -12,6 +12,7 @@ #include #include #include +#include #define CS35L56_DEVID 0x0000000 #define CS35L56_REVID 0x0000004 @@ -23,6 +24,9 @@ #define CS35L56_BLOCK_ENABLES2 0x000201C #define CS35L56_REFCLK_INPUT 0x0002C04 #define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C +#define CS35L56_OTP_MEM_53 0x00300D4 +#define CS35L56_OTP_MEM_54 0x00300D8 +#define CS35L56_OTP_MEM_55 0x00300DC #define CS35L56_ASP1_ENABLES1 0x0004800 #define CS35L56_ASP1_CONTROL1 0x0004804 #define CS35L56_ASP1_CONTROL2 0x0004808 @@ -262,6 +266,9 @@ struct cs35l56_base { bool fw_patched; bool secured; bool can_hibernate; + bool cal_data_valid; + s8 cal_index; + struct cirrus_amp_cal_data cal_data; struct gpio_desc *reset_gpio; }; @@ -269,6 +276,8 @@ extern struct regmap_config cs35l56_regmap_i2c; extern struct regmap_config cs35l56_regmap_spi; extern struct regmap_config cs35l56_regmap_sdw; +extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls; + extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; @@ -286,6 +295,7 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base); int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire); void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp); +int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base); int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version); int cs35l56_hw_init(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 109848a7a413..3cc78920b196 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -798,6 +798,7 @@ config SND_SOC_CS35L56 tristate config SND_SOC_CS35L56_SHARED + select SND_SOC_CS_AMP_LIB tristate config SND_SOC_CS35L56_I2C diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 995d979b6d87..517beaad5cd5 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -5,10 +5,12 @@ // Copyright (C) 2023 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. +#include #include #include #include #include +#include #include "cs35l56.h" @@ -36,6 +38,8 @@ int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { + /* no defaults for OTP_MEM - first read populates cache */ + { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, { CS35L56_ASP1_CONTROL2, 0x18180200 }, @@ -91,6 +95,9 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) case CS35L56_BLOCK_ENABLES2: case CS35L56_REFCLK_INPUT: case CS35L56_GLOBAL_SAMPLE_RATE: + case CS35L56_OTP_MEM_53: + case CS35L56_OTP_MEM_54: + case CS35L56_OTP_MEM_55: case CS35L56_ASP1_ENABLES1: case CS35L56_ASP1_CONTROL1: case CS35L56_ASP1_CONTROL2: @@ -628,6 +635,81 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds } EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); +struct cs35l56_pte { + u8 x; + u8 wafer_id; + u8 pte[2]; + u8 lot[3]; + u8 y; + u8 unused[3]; + u8 dvs; +} __packed; +static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0); + +static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) +{ + struct cs35l56_pte pte; + u64 unique_id; + int ret; + + ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte)); + if (ret) { + dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret); + return ret; + } + + unique_id = pte.lot[2] | (pte.lot[1] << 8) | (pte.lot[0] << 16); + unique_id <<= 32; + unique_id |= pte.x | (pte.y << 8) | (pte.wafer_id << 16) | (pte.dvs << 24); + + dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id); + + *uid = unique_id; + + return 0; +} + +/* Firmware calibration controls */ +const struct cirrus_amp_cal_controls cs35l56_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED); + +int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) +{ + u64 silicon_uid; + int ret; + + /* Driver can't apply calibration to a secured part, so skip */ + if (cs35l56_base->secured) + return 0; + + ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid); + if (ret < 0) + return ret; + + ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid, + cs35l56_base->cal_index, + &cs35l56_base->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) + return ret; + + cs35l56_base->cal_data_valid = true; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, SND_SOC_CS35L56_SHARED); + int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base, bool *fw_missing, unsigned int *fw_version) { @@ -922,3 +1004,4 @@ MODULE_DESCRIPTION("ASoC CS35L56 Shared"); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_AUTHOR("Simon Trimmer "); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); -- cgit v1.2.3 From 1326444e93c250ff99eba048f699313ba6acbf2f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 Feb 2024 15:39:08 +0000 Subject: ASoC: cs35l56: Apply amp calibration from EFI data If there are factory calibration settings in EFI, extract the settings and write them to the firmware calibration controls. This must be done after any firmware or coefficients have been downloaded to the amp. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240223153910.2063698-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 20 +++++++++++++++++++ sound/soc/codecs/cs35l56.c | 44 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index ab960a1c171e..eaa4c706f3a2 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -161,6 +161,20 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; +static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56) +{ + int ret; + + /* SoundWire UniqueId is used to index the calibration array */ + ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0); + if (ret < 0) + return ret; + + cs35l56->base.cal_index = ret & 0xf; + + return 0; +} + static void cs35l56_sdw_init(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -168,6 +182,12 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) pm_runtime_get_noresume(cs35l56->base.dev); + if (cs35l56->base.cal_index < 0) { + ret = cs35l56_sdw_set_cal_index(cs35l56); + if (ret < 0) + goto out; + } + regcache_cache_only(cs35l56->base.regmap, false); ret = cs35l56_init(cs35l56); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 2c1313e34cce..23da9b96d8a7 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -802,16 +803,44 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { } }; +static int cs35l56_write_cal(struct cs35l56_private *cs35l56) +{ + int ret; + + if (cs35l56->base.secured || !cs35l56->base.cal_data_valid) + return -ENODATA; + + ret = wm_adsp_run(&cs35l56->dsp); + if (ret) + return ret; + + ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp, + &cs35l56_calibration_controls, + &cs35l56->base.cal_data); + + wm_adsp_stop(&cs35l56->dsp); + + if (ret == 0) + dev_info(cs35l56->base.dev, "Calibration applied\n"); + + return ret; +} + static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56) { int ret; /* Use wm_adsp to load and apply the firmware patch and coefficient files */ ret = wm_adsp_power_up(&cs35l56->dsp, true); - if (ret) + if (ret) { dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); - else - cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + return; + } + + cs35l56_write_cal(cs35l56); + + /* Always REINIT after applying patch or coefficients */ + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); } static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing) @@ -874,6 +903,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing CS35L56_FIRMWARE_MISSING); cs35l56->base.fw_patched = true; + if (cs35l56_write_cal(cs35l56) == 0) + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + err_unlock: mutex_unlock(&cs35l56->base.irq_lock); err: @@ -1356,6 +1388,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) init_completion(&cs35l56->init_completion); mutex_init(&cs35l56->base.irq_lock); + cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; dev_set_drvdata(cs35l56->base.dev, cs35l56); @@ -1457,6 +1490,10 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (ret) return ret; + ret = cs35l56_get_calibration(&cs35l56->base); + if (ret) + return ret; + if (!cs35l56->base.reset_gpio) { dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n"); cs35l56->soft_resetting = true; @@ -1541,6 +1578,7 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = { MODULE_DESCRIPTION("ASoC CS35L56 driver"); MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_AUTHOR("Simon Trimmer "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 051e887264b3e161cf2c1e163321b31191bf78a4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 Feb 2024 12:59:24 +0100 Subject: ASoC: codecs: tx-macro: split widgets per different LPASS versions TX macro codec differs slightly between different Qualcomm Low Power Audio SubSystem (LPASS) block versions. In LPASS version 9.2 the register responsible for TX SMIC MUXn muxes is different, thus to properly support it, the driver needs to register different widgets per different LPASS version. Prepare for supporting this register difference by refactoring existing code: 1. Move few widgets (TX SMIC MUXn, TX SWR_ADCn, TX SWR_DMICn) out of common 'tx_macro_dapm_widgets[]' array to a new per-variant specific array 'tx_macro_dapm_widgets_v9[]'. 2. Move also related audio routes into new array. 3. Store pointers to these variant-specific arrays in new variant-data structure 'tx_macro_data'. 4. Add variant-specific widgets and routes in component probe, instead of driver probe. The change should have no real impact, except re-shuffling code and registering some widgets and audio routes in component probe, instead of driver probe. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240226115925.53953-2-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.h | 6 + sound/soc/codecs/lpass-tx-macro.c | 377 ++++++++++++++++++++-------------- 2 files changed, 232 insertions(+), 151 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index d3684c7ab930..18f5b8c8e822 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -11,6 +11,12 @@ /* The soundwire block should be internally reset at probe */ #define LPASS_MACRO_FLAG_RESET_SWR BIT(1) +enum lpass_version { + LPASS_VER_9_0_0, + LPASS_VER_10_0_0, + LPASS_VER_11_0_0, +}; + struct lpass_macro { struct device *macro_pd; struct device *dcodec_pd; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 7e51212d4503..d6b3b6bb6923 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -255,8 +255,18 @@ struct hpf_work { struct delayed_work dwork; }; +struct tx_macro_data { + unsigned int flags; + unsigned int ver; + const struct snd_soc_dapm_widget *extra_widgets; + size_t extra_widgets_num; + const struct snd_soc_dapm_route *extra_routes; + size_t extra_routes_num; +}; + struct tx_macro { struct device *dev; + const struct tx_macro_data *data; struct snd_soc_component *component; struct hpf_work tx_hpf_work[NUM_DECIMATORS]; struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; @@ -1237,53 +1247,6 @@ static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_d static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum); static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum); -static const char * const smic_mux_text[] = { - "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", - "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", - "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" -}; - -static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0, - 0, smic_mux_text); - -static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0, - 0, smic_mux_text); - -static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); -static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum, - snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); - static const char * const dmic_mux_text[] = { "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "DMIC7" @@ -1429,15 +1392,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0, tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)), - SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux), - SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux), - SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux), SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux), SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux), @@ -1447,18 +1401,6 @@ static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux), SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux), - SND_SOC_DAPM_INPUT("TX SWR_ADC0"), - SND_SOC_DAPM_INPUT("TX SWR_ADC1"), - SND_SOC_DAPM_INPUT("TX SWR_ADC2"), - SND_SOC_DAPM_INPUT("TX SWR_ADC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), - SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), SND_SOC_DAPM_INPUT("TX DMIC0"), SND_SOC_DAPM_INPUT("TX DMIC1"), SND_SOC_DAPM_INPUT("TX DMIC2"), @@ -1580,6 +1522,150 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, +}; + +/* Controls and routes specific to LPASS <= v9.0.0 */ +static const char * const smic_mux_text_v9[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", + "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", + "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9); + +static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9[] = { {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, @@ -1595,16 +1681,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, - {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, - {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, @@ -1620,16 +1696,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, - {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, - {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, @@ -1645,16 +1711,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, - {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, - {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, @@ -1670,16 +1726,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, - {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, - {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, @@ -1695,16 +1741,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, - {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, - {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, @@ -1720,16 +1756,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, - {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, - {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, @@ -1745,16 +1771,6 @@ static const struct snd_soc_dapm_route tx_audio_map[] = { {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, - {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, - {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, - {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, - {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, - {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, - {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, - {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, - {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, - {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, - {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, @@ -1825,10 +1841,41 @@ static const struct snd_kcontrol_new tx_macro_snd_controls[] = { tx_macro_get_bcs, tx_macro_set_bcs), }; +static int tx_macro_component_extend(struct snd_soc_component *comp) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp); + struct tx_macro *tx = snd_soc_component_get_drvdata(comp); + int ret; + + if (tx->data->extra_widgets_num) { + ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets, + tx->data->extra_widgets_num); + if (ret) { + dev_err(tx->dev, "failed to add extra widgets: %d\n", ret); + return ret; + } + } + + if (tx->data->extra_routes_num) { + ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes, + tx->data->extra_routes_num); + if (ret) { + dev_err(tx->dev, "failed to add extra routes: %d\n", ret); + return ret; + } + } + + return 0; +} + static int tx_macro_component_probe(struct snd_soc_component *comp) { struct tx_macro *tx = snd_soc_component_get_drvdata(comp); - int i; + int i, ret; + + ret = tx_macro_component_extend(comp); + if (ret) + return ret; snd_soc_component_init_regmap(comp, tx->regmap); @@ -1958,17 +2005,16 @@ static int tx_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - kernel_ulong_t flags; struct tx_macro *tx; void __iomem *base; int ret, reg; - flags = (kernel_ulong_t)device_get_match_data(dev); - tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL); if (!tx) return -ENOMEM; + tx->data = device_get_match_data(dev); + tx->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(tx->macro)) return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); @@ -1981,7 +2027,7 @@ static int tx_macro_probe(struct platform_device *pdev) if (IS_ERR(tx->mclk)) return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); - if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { tx->npl = devm_clk_get(dev, "npl"); if (IS_ERR(tx->npl)) return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); @@ -2056,7 +2102,7 @@ static int tx_macro_probe(struct platform_device *pdev) /* reset soundwire block */ - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); @@ -2064,7 +2110,7 @@ static int tx_macro_probe(struct platform_device *pdev) CDC_TX_SWR_CLK_EN_MASK, CDC_TX_SWR_CLK_ENABLE); - if (flags & LPASS_MACRO_FLAG_RESET_SWR) + if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR) regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, CDC_TX_SWR_RESET_MASK, 0x0); @@ -2168,25 +2214,54 @@ static const struct dev_pm_ops tx_macro_pm_ops = { SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) }; +static const struct tx_macro_data lpass_ver_9 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), + .extra_routes = tx_audio_map_v9, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), +}; + +static const struct tx_macro_data lpass_ver_10_sm6115 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .ver = LPASS_VER_10_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), + .extra_routes = tx_audio_map_v9, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), +}; + + +static const struct tx_macro_data lpass_ver_11 = { + .flags = LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_11_0_0, + .extra_widgets = tx_macro_dapm_widgets_v9, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), + .extra_routes = tx_audio_map_v9, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), +}; + static const struct of_device_id tx_macro_dt_match[] = { { .compatible = "qcom,sc7280-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm6115-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + .data = &lpass_ver_10_sm6115, }, { .compatible = "qcom,sm8250-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm8450-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { .compatible = "qcom,sm8550-lpass-tx-macro", - .data = (void *)LPASS_MACRO_FLAG_RESET_SWR, + .data = &lpass_ver_11, }, { .compatible = "qcom,sc8280xp-lpass-tx-macro", - .data = (void *)(LPASS_MACRO_FLAG_HAS_NPL_CLOCK | LPASS_MACRO_FLAG_RESET_SWR), + .data = &lpass_ver_9, }, { } }; -- cgit v1.2.3 From d34f0c8ee2e30b8a1470ce635289591148552a93 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 26 Feb 2024 12:59:25 +0100 Subject: ASoC: codecs: tx-macro: correct TX SMIC MUXn widgets on SM8350+ Starting with Qualcomm SM8350 SoC, so Low Power Audio SubSystem (LPASS) block version v9.2, the register responsible for TX SMIC MUXn muxes is different. In earlier LPASS versions this mux had bit fields for analogue (ADCn) and digital (SWR_DMICn) MICs. Choice of ADCn was selecting the analogue path in CDC_TX_TOP_CSR_SWR_DMICn_CTL register. With LPASS v9.2 and newer, the bit fields are integrated into just SWR_MICn and there is no distinction for analogue or digital MIC in the register. Fix support for LPASS v9.2+: 1. Add new set of widgets and audio routes for LPASS v9.2. 2. Do not choose analogue or digital in CDC_TX_TOP_CSR_SWR_DMICn_CTL based on value of the mux. 3. Replace all the input widgets (TX SWR_ADCn, TX SWR_DMICn) with TX SWR_INPUTn ones. The change is not backwards compatible with older DTBs and existing mixer settings, therefore it does not change handling of older platforms with working micrphones (SC8280xp) but only the ones with issues (SM8450, SM8550) which need the fix. Signed-off-by: Krzysztof Kozlowski Link: https://msgid.link/r/20240226115925.53953-3-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/lpass-macro-common.h | 1 + sound/soc/codecs/lpass-tx-macro.c | 322 ++++++++++++++++++++++++++++++---- 2 files changed, 292 insertions(+), 31 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index 18f5b8c8e822..d98718b3dc4b 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -13,6 +13,7 @@ enum lpass_version { LPASS_VER_9_0_0, + LPASS_VER_9_2_0, LPASS_VER_10_0_0, LPASS_VER_11_0_0, }; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index d6b3b6bb6923..c98b0b747a92 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -648,13 +648,18 @@ exit: return 0; } -static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator) +static bool is_amic_enabled(struct snd_soc_component *component, + struct tx_macro *tx, u8 decimator) { u16 adc_mux_reg, adc_reg, adc_n; adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator); if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) { + if (tx->data->ver > LPASS_VER_9_0_0) + return true; + + /* else: LPASS <= v9.0.0 */ adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator); adc_n = snd_soc_component_read_field(component, adc_reg, CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK); @@ -683,7 +688,7 @@ static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator); hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator); - if (is_amic_enabled(component, hpf_work->decimator)) { + if (is_amic_enabled(component, tx, hpf_work->decimator)) { snd_soc_component_write_field(component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, @@ -747,15 +752,61 @@ static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, return 0; } +static void tx_macro_update_smic_sel_v9(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (val < 5) { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } +} + +static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, + struct tx_macro *tx, u16 mic_sel_reg, + unsigned int val) +{ + unsigned int dmic; + u16 dmic_clk_reg; + + if (widget->shift) { + /* MSM DMIC */ + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + CDC_TX_SWR_MIC_CLK_DEFAULT); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } +} + static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, dmic; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + unsigned int val; u16 mic_sel_reg; - u16 dmic_clk_reg; val = ucontrol->value.enumerated.item[0]; if (val >= e->items) @@ -792,21 +843,15 @@ static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, } if (val != 0) { - if (widget->shift) { /* MSM DMIC */ - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 1); - } else if (val < 5) { - snd_soc_component_write_field(component, mic_sel_reg, - CDC_TXn_ADC_DMIC_SEL_MASK, 0); - } else { + if (widget->shift) /* MSM DMIC */ snd_soc_component_write_field(component, mic_sel_reg, CDC_TXn_ADC_DMIC_SEL_MASK, 1); - dmic = TX_ADC_TO_DMIC(val); - dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); - snd_soc_component_write_field(component, dmic_clk_reg, - CDC_TX_SWR_DMIC_CLK_SEL_MASK, - CDC_TX_SWR_MIC_CLK_DEFAULT); - } + else if (tx->data->ver <= LPASS_VER_9_0_0) + tx_macro_update_smic_sel_v9(component, widget, tx, + mic_sel_reg, val); + else + tx_macro_update_smic_sel_v9_2(component, widget, tx, + mic_sel_reg, val); } return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); @@ -907,7 +952,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: snd_soc_component_write_field(component, tx_vol_ctl_reg, CDC_TXn_CLK_EN_MASK, 0x1); - if (!is_amic_enabled(component, decimator)) { + if (!is_amic_enabled(component, tx, decimator)) { snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00); /* Minimum 1 clk cycle delay is required as per HW spec */ usleep_range(1000, 1010); @@ -923,7 +968,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_CUT_FREQ_MASK, CF_MIN_3DB_150HZ); - if (is_amic_enabled(component, decimator)) { + if (is_amic_enabled(component, tx, decimator)) { hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS; unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS; } @@ -939,7 +984,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, 0x02); - if (!is_amic_enabled(component, decimator)) + if (!is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | CDC_TXn_HPF_ZERO_GATE_MASK, @@ -976,7 +1021,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, component, dec_cfg_reg, CDC_TXn_HPF_CUT_FREQ_MASK, hpf_cut_off_freq); - if (is_amic_enabled(component, decimator)) + if (is_amic_enabled(component, tx, decimator)) snd_soc_component_update_bits(component, hpf_gate_reg, CDC_TXn_HPF_F_CHANGE_MASK | @@ -1787,6 +1832,200 @@ static const struct snd_soc_dapm_route tx_audio_map_v9[] = { {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, }; +/* Controls and routes specific to LPASS >= v9.2.0 */ +static const char * const smic_mux_text_v9_2[] = { + "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3", + "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7", + "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text_v9_2); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text_v9_2); + +static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = { + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2), + + SND_SOC_DAPM_INPUT("TX SWR_INPUT0"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT1"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT2"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT3"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT4"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT5"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT6"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT7"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT8"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT9"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT10"), + SND_SOC_DAPM_INPUT("TX SWR_INPUT11"), +}; + +static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = { + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"}, + {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"}, + {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"}, + {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"}, + {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"}, + {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"}, + {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"}, + {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"}, + {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"}, + {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"}, + {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"}, + {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"}, + {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"}, +}; + static const struct snd_kcontrol_new tx_macro_snd_controls[] = { SOC_SINGLE_S8_TLV("TX_DEC0 Volume", CDC_TX0_TX_VOL_CTL, @@ -2224,27 +2463,42 @@ static const struct tx_macro_data lpass_ver_9 = { .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), }; +static const struct tx_macro_data lpass_ver_9_2 = { + .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK | + LPASS_MACRO_FLAG_RESET_SWR, + .ver = LPASS_VER_9_2_0, + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), +}; + static const struct tx_macro_data lpass_ver_10_sm6115 = { .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK, .ver = LPASS_VER_10_0_0, - .extra_widgets = tx_macro_dapm_widgets_v9, - .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), - .extra_routes = tx_audio_map_v9, - .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), }; - static const struct tx_macro_data lpass_ver_11 = { .flags = LPASS_MACRO_FLAG_RESET_SWR, .ver = LPASS_VER_11_0_0, - .extra_widgets = tx_macro_dapm_widgets_v9, - .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9), - .extra_routes = tx_audio_map_v9, - .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9), + .extra_widgets = tx_macro_dapm_widgets_v9_2, + .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2), + .extra_routes = tx_audio_map_v9_2, + .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2), }; static const struct of_device_id tx_macro_dt_match[] = { { + /* + * The block is actually LPASS v9.4, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC7280 fine, so apparently the fix + * is not necessary. + */ .compatible = "qcom,sc7280-lpass-tx-macro", .data = &lpass_ver_9, }, { @@ -2255,12 +2509,18 @@ static const struct of_device_id tx_macro_dt_match[] = { .data = &lpass_ver_9, }, { .compatible = "qcom,sm8450-lpass-tx-macro", - .data = &lpass_ver_9, + .data = &lpass_ver_9_2, }, { .compatible = "qcom,sm8550-lpass-tx-macro", .data = &lpass_ver_11, }, { .compatible = "qcom,sc8280xp-lpass-tx-macro", + /* + * The block is actually LPASS v9.3, but keep LPASS v9 match + * data and audio widgets, due to compatibility reasons. + * Microphones are working on SC8280xp fine, so apparently the + * fix is not necessary. + */ .data = &lpass_ver_9, }, { } -- cgit v1.2.3 From e3741a8d28a1137f8b19ae6f3d6e3be69a454a0a Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 23 Feb 2024 18:51:07 +0100 Subject: ASoC: meson: axg-tdm-interface: fix mclk setup without mclk-fs By default, when mclk-fs is not provided, the tdm-interface driver requests an MCLK that is 4x the bit clock, SCLK. However there is no justification for this: * If the codec needs MCLK for its operation, mclk-fs is expected to be set according to the codec requirements. * If the codec does not need MCLK the minimum is 2 * SCLK, because this is minimum the divider between SCLK and MCLK can do. Multiplying by 4 may cause problems because the PLL limit may be reached sooner than it should, so use 2x instead. Fixes: d60e4f1e4be5 ("ASoC: meson: add tdm interface driver") Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240223175116.2005407-2-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 1c3d433cefd2..cd5168e826df 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -264,8 +264,8 @@ static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, srate = iface->slots * iface->slot_width * params_rate(params); if (!iface->mclk_rate) { - /* If no specific mclk is requested, default to bit clock * 4 */ - clk_set_rate(iface->mclk, 4 * srate); + /* If no specific mclk is requested, default to bit clock * 2 */ + clk_set_rate(iface->mclk, 2 * srate); } else { /* Check if we can actually get the bit clock from mclk */ if (iface->mclk_rate % srate) { -- cgit v1.2.3 From 59c6a3a43b221cc2a211181b1298e43b2c2df782 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 23 Feb 2024 18:51:08 +0100 Subject: ASoC: meson: axg-tdm-interface: add frame rate constraint According to Amlogic datasheets for the SoCs supported by this driver, the maximum bit clock rate is 100MHz. The tdm interface allows the rates listed by the DAI driver, regardless of the number slots or their width. However, these will impact the bit clock rate. Hitting the 100MHz limit is very unlikely for most use cases but it is possible. For example with 32 slots / 32 bits wide, the maximum rate is no longer 384kHz but ~96kHz. Add the constraint accordingly if the component is not already active. If it is active, the rate is already constrained by the first stream rate. Fixes: d60e4f1e4be5 ("ASoC: meson: add tdm interface driver") Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240223175116.2005407-3-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index cd5168e826df..2cedbce73837 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -12,6 +12,9 @@ #include "axg-tdm.h" +/* Maximum bit clock frequency according the datasheets */ +#define MAX_SCLK 100000000 /* Hz */ + enum { TDM_IFACE_PAD, TDM_IFACE_LOOPBACK, @@ -153,19 +156,27 @@ static int axg_tdm_iface_startup(struct snd_pcm_substream *substream, return -EINVAL; } - /* Apply component wide rate symmetry */ if (snd_soc_component_active(dai->component)) { + /* Apply component wide rate symmetry */ ret = snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, iface->rate); - if (ret < 0) { - dev_err(dai->dev, - "can't set iface rate constraint\n"); - return ret; - } + + } else { + /* Limit rate according to the slot number and width */ + unsigned int max_rate = + MAX_SCLK / (iface->slots * iface->slot_width); + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, max_rate); } - return 0; + if (ret < 0) + dev_err(dai->dev, "can't set iface rate constraint\n"); + else + ret = 0; + + return ret; } static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, -- cgit v1.2.3 From 48bbec092e4cf2fe1d3f81a889ec176e83aee695 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 23 Feb 2024 18:51:09 +0100 Subject: ASoC: meson: axg-tdm-interface: update error format error traces ASoC stopped using CBS_CFS and CBM_CFM a few years ago but the traces in the amlogic tdm interface driver did not follow. Update this to match the new format names Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240223175116.2005407-4-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 2cedbce73837..bf708717635b 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -133,7 +133,7 @@ static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_BP_FC: case SND_SOC_DAIFMT_BC_FP: - dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); + dev_err(dai->dev, "only BP_FP and BC_FC are supported\n"); fallthrough; default: return -EINVAL; -- cgit v1.2.3 From a2417b6c0f9c3cc914c88face9abd6e9b9d76c00 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 23 Feb 2024 18:51:10 +0100 Subject: ASoC: meson: axg-spdifin: use max width for rate detection Use maximum width between 2 edges to setup spdifin thresholds and detect the input sample rate. This comes from Amlogic SDK and seems to be marginally more reliable than minimum width. This is done to align with a future eARC support. No issue was reported with minimum width so far, this is considered to be an update so no Fixes tag is set. Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240223175116.2005407-5-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-spdifin.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index bc2f2849ecfb..e721f579321e 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -179,9 +179,9 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, SPDIFIN_CTRL1_BASE_TIMER, FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000)); - /* Threshold based on the minimum width between two edges */ + /* Threshold based on the maximum width between two edges */ regmap_update_bits(priv->map, SPDIFIN_CTRL0, - SPDIFIN_CTRL0_WIDTH_SEL, SPDIFIN_CTRL0_WIDTH_SEL); + SPDIFIN_CTRL0_WIDTH_SEL, 0); /* Calculate the last timer which has no threshold */ t_next = axg_spdifin_mode_timer(priv, i, rate); @@ -199,7 +199,7 @@ static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, axg_spdifin_write_timer(priv->map, i, t); /* Set the threshold value */ - axg_spdifin_write_threshold(priv->map, i, t + t_next); + axg_spdifin_write_threshold(priv->map, i, 3 * (t + t_next)); /* Save the current timer for the next threshold calculation */ t_next = t; -- cgit v1.2.3 From 8b410b3c46128f1eee78f1182731b84d9d2e79ef Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 23 Feb 2024 18:51:11 +0100 Subject: ASoC: meson: axg-fifo: take continuous rates The rate of the stream does not matter for the fifos of the axg family. Fifos will just push or pull data to/from the DDR according to consumption or production of the downstream element, which is the DPCM backend. Drop the rate list and allow continuous rates. The lower and upper rate are set according what is known to work with the different backends This allows the PDM input backend to also use continuous rates. Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240223175116.2005407-6-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.h | 2 -- sound/soc/meson/axg-frddr.c | 8 ++++++-- sound/soc/meson/axg-toddr.c | 8 ++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index df528e8cb7c9..a14c31eb06d8 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -21,8 +21,6 @@ struct snd_soc_dai_driver; struct snd_soc_pcm_runtime; #define AXG_FIFO_CH_MAX 128 -#define AXG_FIFO_RATES (SNDRV_PCM_RATE_5512 | \ - SNDRV_PCM_RATE_8000_384000) #define AXG_FIFO_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 8c166a5f338c..98140f449eb3 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -109,7 +109,9 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .stream_name = "Playback", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, @@ -184,7 +186,9 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .stream_name = "Playback", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index 1a0be177b8fe..32ee45cce7f8 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -131,7 +131,9 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .stream_name = "Capture", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, @@ -226,7 +228,9 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .stream_name = "Capture", .channels_min = 1, .channels_max = AXG_FIFO_CH_MAX, - .rates = AXG_FIFO_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5515, + .rate_max = 384000, .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, -- cgit v1.2.3 From cb9d8a2c6cb7cbb0fc919defe4fae741bfcae9de Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 27 Feb 2024 10:00:42 +0000 Subject: ASoC: cs35l56: Prevent bad sign extension in cs35l56_read_silicon_uid() Cast u8 values to u32 when using them to build a 32-bit unsigned value that is then stored in a u64. This avoids the possibility of a bad sign extension where the u8 is implicitly extended to an int, thus changing it from an unsigned to a signed value. Whether this is a real problem is debatable, but it does no harm to ensure that the u8 are cast to a suitable type for shifting. Signed-off-by: Richard Fitzgerald Fixes: e1830f66f6c6 ("ASoC: cs35l56: Add helper functions for amp calibration") Link: https://msgid.link/r/20240227100042.99-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 517beaad5cd5..e15f78c2bd83 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -658,9 +658,10 @@ static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid) return ret; } - unique_id = pte.lot[2] | (pte.lot[1] << 8) | (pte.lot[0] << 16); + unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16); unique_id <<= 32; - unique_id |= pte.x | (pte.y << 8) | (pte.wafer_id << 16) | (pte.dvs << 24); + unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) | + ((u32)pte.dvs << 24); dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id); -- cgit v1.2.3 From 9e6f39535c794adea6ba802a52c722d193c28124 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 27 Feb 2024 16:08:25 +0100 Subject: ASoC: meson: axg-fifo: use FIELD helpers Use FIELD_GET() and FIELD_PREP() helpers instead of doing it manually. Signed-off-by: Jerome Brunet Link: https://msgid.link/r/20240227150826.573581-1-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.c | 25 +++++++++++++------------ sound/soc/meson/axg-fifo.h | 12 +++++------- sound/soc/meson/axg-frddr.c | 5 +++-- sound/soc/meson/axg-toddr.c | 22 ++++++++++------------ 4 files changed, 31 insertions(+), 33 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index 65541fdb0038..bebee0ca8e38 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -3,6 +3,7 @@ // Copyright (c) 2018 BayLibre, SAS. // Author: Jerome Brunet +#include #include #include #include @@ -145,8 +146,8 @@ int axg_fifo_pcm_hw_params(struct snd_soc_component *component, /* Enable irq if necessary */ irq_en = runtime->no_period_wakeup ? 0 : FIFO_INT_COUNT_REPEAT; regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), - CTRL0_INT_EN(irq_en)); + CTRL0_INT_EN, + FIELD_PREP(CTRL0_INT_EN, irq_en)); return 0; } @@ -176,9 +177,9 @@ int axg_fifo_pcm_hw_free(struct snd_soc_component *component, { struct axg_fifo *fifo = axg_fifo_data(ss); - /* Disable the block count irq */ + /* Disable irqs */ regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_COUNT_REPEAT), 0); + CTRL0_INT_EN, 0); return 0; } @@ -187,13 +188,13 @@ EXPORT_SYMBOL_GPL(axg_fifo_pcm_hw_free); static void axg_fifo_ack_irq(struct axg_fifo *fifo, u8 mask) { regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_INT_CLR(FIFO_INT_MASK), - CTRL1_INT_CLR(mask)); + CTRL1_INT_CLR, + FIELD_PREP(CTRL1_INT_CLR, mask)); /* Clear must also be cleared */ regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_INT_CLR(FIFO_INT_MASK), - 0); + CTRL1_INT_CLR, + FIELD_PREP(CTRL1_INT_CLR, 0)); } static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) @@ -204,7 +205,7 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) regmap_read(fifo->map, FIFO_STATUS1, &status); - status = STATUS1_INT_STS(status) & FIFO_INT_MASK; + status = FIELD_GET(STATUS1_INT_STS, status); if (status & FIFO_INT_COUNT_REPEAT) snd_pcm_period_elapsed(ss); else @@ -254,15 +255,15 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, /* Setup status2 so it reports the memory pointer */ regmap_update_bits(fifo->map, FIFO_CTRL1, - CTRL1_STATUS2_SEL_MASK, - CTRL1_STATUS2_SEL(STATUS2_SEL_DDR_READ)); + CTRL1_STATUS2_SEL, + FIELD_PREP(CTRL1_STATUS2_SEL, STATUS2_SEL_DDR_READ)); /* Make sure the dma is initially disabled */ __dma_enable(fifo, false); /* Disable irqs until params are ready */ regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_INT_EN(FIFO_INT_MASK), 0); + CTRL0_INT_EN, 0); /* Clear any pending interrupt */ axg_fifo_ack_irq(fifo, FIFO_INT_MASK); diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index a14c31eb06d8..4c48c0a08481 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -40,21 +40,19 @@ struct snd_soc_pcm_runtime; #define FIFO_CTRL0 0x00 #define CTRL0_DMA_EN BIT(31) -#define CTRL0_INT_EN(x) ((x) << 16) +#define CTRL0_INT_EN GENMASK(23, 16) #define CTRL0_SEL_MASK GENMASK(2, 0) #define CTRL0_SEL_SHIFT 0 #define FIFO_CTRL1 0x04 -#define CTRL1_INT_CLR(x) ((x) << 0) -#define CTRL1_STATUS2_SEL_MASK GENMASK(11, 8) -#define CTRL1_STATUS2_SEL(x) ((x) << 8) +#define CTRL1_INT_CLR GENMASK(7, 0) +#define CTRL1_STATUS2_SEL GENMASK(11, 8) #define STATUS2_SEL_DDR_READ 0 -#define CTRL1_FRDDR_DEPTH_MASK GENMASK(31, 24) -#define CTRL1_FRDDR_DEPTH(x) ((x) << 24) +#define CTRL1_FRDDR_DEPTH GENMASK(31, 24) #define FIFO_START_ADDR 0x08 #define FIFO_FINISH_ADDR 0x0c #define FIFO_INT_ADDR 0x10 #define FIFO_STATUS1 0x14 -#define STATUS1_INT_STS(x) ((x) << 0) +#define STATUS1_INT_STS GENMASK(7, 0) #define FIFO_STATUS2 0x18 #define FIFO_INIT_ADDR 0x24 #define FIFO_CTRL2 0x28 diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 98140f449eb3..e97d43ae7fd2 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -7,6 +7,7 @@ * This driver implements the frontend playback DAI of AXG and G12A based SoCs */ +#include #include #include #include @@ -59,8 +60,8 @@ static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream, /* Trim the FIFO depth if the period is small to improve latency */ depth = min(period, fifo->depth); val = (depth / AXG_FIFO_BURST) - 1; - regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH_MASK, - CTRL1_FRDDR_DEPTH(val)); + regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH, + FIELD_PREP(CTRL1_FRDDR_DEPTH, val)); return 0; } diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index 32ee45cce7f8..e03a6e21c1c6 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -5,6 +5,7 @@ /* This driver implements the frontend capture DAI of AXG based SoCs */ +#include #include #include #include @@ -19,12 +20,9 @@ #define CTRL0_TODDR_EXT_SIGNED BIT(29) #define CTRL0_TODDR_PP_MODE BIT(28) #define CTRL0_TODDR_SYNC_CH BIT(27) -#define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) -#define CTRL0_TODDR_TYPE(x) ((x) << 13) -#define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) -#define CTRL0_TODDR_MSB_POS(x) ((x) << 8) -#define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) -#define CTRL0_TODDR_LSB_POS(x) ((x) << 3) +#define CTRL0_TODDR_TYPE GENMASK(15, 13) +#define CTRL0_TODDR_MSB_POS GENMASK(12, 8) +#define CTRL0_TODDR_LSB_POS GENMASK(7, 3) #define CTRL1_TODDR_FORCE_FINISH BIT(25) #define CTRL1_SEL_SHIFT 28 @@ -76,12 +74,12 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, width = params_width(params); regmap_update_bits(fifo->map, FIFO_CTRL0, - CTRL0_TODDR_TYPE_MASK | - CTRL0_TODDR_MSB_POS_MASK | - CTRL0_TODDR_LSB_POS_MASK, - CTRL0_TODDR_TYPE(type) | - CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | - CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); + CTRL0_TODDR_TYPE | + CTRL0_TODDR_MSB_POS | + CTRL0_TODDR_LSB_POS, + FIELD_PREP(CTRL0_TODDR_TYPE, type) | + FIELD_PREP(CTRL0_TODDR_MSB_POS, TODDR_MSB_POS) | + FIELD_PREP(CTRL0_TODDR_LSB_POS, TODDR_MSB_POS - (width - 1))); return 0; } -- cgit v1.2.3 From cf9c19df2755d57501ce3922bb22ff0734d8bed5 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 26 Feb 2024 13:44:29 +0100 Subject: ASoC: codecs: hda: Skip HDMI/DP registration if i915 is missing If i915 does not support given platform but the hardware i.e.: HDAudio codec is still there, the codec-probing procedure will succeed for such device but the follow up initialization will always end up with -ENODEV. While bus could filter out address '2' which Intel's HDMI/DP codecs always enumerate on, more robust approach is to check for i915 presence before registering display codecs. Signed-off-by: Cezary Rojewski Acked-by: Mark Brown Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20240226124432.1203798-3-cezary.rojewski@intel.com --- sound/soc/codecs/hda.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index d2117e36ddd1..7c6bedeceaa2 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -350,6 +350,11 @@ static int hda_hdev_attach(struct hdac_device *hdev) struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); struct snd_soc_component_driver *comp_drv; + if (hda_codec_is_display(codec) && !hdev->bus->audio_component) { + dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id); + return -ENODEV; + } + comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL); if (!comp_drv) return -ENOMEM; -- cgit v1.2.3 From b9f706f9ef468754d35a459eaff12cc0594b6e5d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 26 Feb 2024 13:44:30 +0100 Subject: ASoC: Intel: avs: Ignore codecs with no suppoting driver HDMI codecs which are present and functional from audio perspective lack i915 support on drm side what results in -ENODEV during the probing sequence. There is no reason to perform recovery procedure e.g.: reset the HDAudio controller if this is the case. Signed-off-by: Cezary Rojewski Acked-by: Mark Brown Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20240226124432.1203798-4-cezary.rojewski@intel.com --- sound/soc/intel/avs/core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index db78eb2f0108..565878eb42cd 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -144,7 +144,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) /* configure effectively creates new ASoC component */ ret = snd_hda_codec_configure(codec); if (ret < 0) { - dev_err(bus->dev, "failed to config codec %d\n", ret); + dev_warn(bus->dev, "failed to config codec #%d: %d\n", addr, ret); return ret; } @@ -153,15 +153,16 @@ static int probe_codec(struct hdac_bus *bus, int addr) static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus) { - int c; + int ret, c; /* First try to probe all given codec slots */ for (c = 0; c < HDA_MAX_CODECS; c++) { if (!(bus->codec_mask & BIT(c))) continue; - if (!probe_codec(bus, c)) - /* success, continue probing */ + ret = probe_codec(bus, c); + /* Ignore codecs with no supporting driver. */ + if (!ret || ret == -ENODEV) continue; /* -- cgit v1.2.3 From 3adb233ec8777fd3a37441e363b91b9de6a9c2d2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 26 Feb 2024 13:44:31 +0100 Subject: ASoC: codecs: hda: Cleanup error messages Be cohesive and use same pattern in each error message. Signed-off-by: Cezary Rojewski Acked-by: Mark Brown Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20240226124432.1203798-5-cezary.rojewski@intel.com --- sound/soc/codecs/hda.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index 7c6bedeceaa2..5a58723dc0e9 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -198,19 +198,19 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec, false); if (ret < 0) { - dev_err(&hdev->dev, "create hda codec failed: %d\n", ret); + dev_err(&hdev->dev, "codec create failed: %d\n", ret); goto device_new_err; } ret = snd_hda_codec_set_name(codec, codec->preset->name); if (ret < 0) { - dev_err(&hdev->dev, "name failed %s\n", codec->preset->name); + dev_err(&hdev->dev, "set name: %s failed: %d\n", codec->preset->name, ret); goto err; } ret = snd_hdac_regmap_init(&codec->core); if (ret < 0) { - dev_err(&hdev->dev, "regmap init failed\n"); + dev_err(&hdev->dev, "regmap init failed: %d\n", ret); goto err; } @@ -223,13 +223,13 @@ static int hda_codec_probe(struct snd_soc_component *component) ret = patch(codec); if (ret < 0) { - dev_err(&hdev->dev, "patch failed %d\n", ret); + dev_err(&hdev->dev, "codec init failed: %d\n", ret); goto err; } ret = snd_hda_codec_parse_pcms(codec); if (ret < 0) { - dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + dev_err(&hdev->dev, "unable to map pcms to dai: %d\n", ret); goto parse_pcms_err; } -- cgit v1.2.3 From 177862317a98adde284233aee074fc6e6a51cf95 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 4 Mar 2024 14:37:05 +0000 Subject: ASoC: cs-amp-lib: Add KUnit test for calibration helpers Add a KUnit test for the cs-amp-lib library. This has test cases for cs_amp_get_efi_calibration_data() and cs_amp_write_cal_coeffs(). A KUNIT_STATIC_STUB_REDIRECT() has been added to cs_amp_get_efi_variable() and cs_amp_write_cal_coeff() so that the KUnit test can redirect these to test harness functions. Much of the testing involves invoking the same function with different parameters, i.e. the number of amps and the amp index within the array. This uses parameterization rather than looping. The idea is to avoid looping over configurations within one test case as that has a higher chance of having a bug that doesn't actually test all the expected cases. Having the test run exactly one configuration, and then tear-down, is less prone to accidentally skipped configurations. Signed-off-by: Richard Fitzgerald Link: https://msgid.link/r/20240304143705.26362-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs-amp-lib.h | 14 + sound/soc/codecs/Kconfig | 13 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs-amp-lib-test.c | 709 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs-amp-lib.c | 18 +- 5 files changed, 754 insertions(+), 2 deletions(-) create mode 100644 sound/soc/codecs/cs-amp-lib-test.c (limited to 'sound/soc') diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h index 077fe36885b5..f481148735e1 100644 --- a/include/sound/cs-amp-lib.h +++ b/include/sound/cs-amp-lib.h @@ -49,4 +49,18 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, const struct cirrus_amp_cal_data *data); int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, struct cirrus_amp_cal_data *out_data); + +struct cs_amp_test_hooks { + efi_status_t (*get_efi_variable)(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf); + + int (*write_cal_coeff)(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val); +}; + +extern const struct cs_amp_test_hooks * const cs_amp_test_hooks; + #endif /* CS_AMP_LIB_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 15f287784d8b..f78ea2f86fa6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -732,6 +732,19 @@ config SND_SOC_CROS_EC_CODEC config SND_SOC_CS_AMP_LIB tristate +config SND_SOC_CS_AMP_LIB_TEST + tristate "KUnit test for Cirrus Logic cs-amp-lib" + depends on KUNIT + default KUNIT_ALL_TESTS + select SND_SOC_CS_AMP_LIB + help + This builds KUnit tests for the Cirrus Logic common + amplifier 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_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0fc40640e5d0..7c075539dc47 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -60,6 +60,7 @@ snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o snd-soc-cs-amp-lib-objs := cs-amp-lib.o +snd-soc-cs-amp-lib-test-objs := cs-amp-lib-test.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o @@ -454,6 +455,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o +obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c new file mode 100644 index 000000000000..15f991b2e16e --- /dev/null +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// KUnit test for the Cirrus common amplifier library. +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cs_amp_lib_test_priv { + struct platform_device amp_pdev; + + struct cirrus_amp_efi_data *cal_blob; + struct list_head ctl_write_list; +}; + +struct cs_amp_lib_test_ctl_write_entry { + struct list_head list; + unsigned int value; + char name[16]; +}; + +struct cs_amp_lib_test_param { + int num_amps; + int amp_index; +}; + +static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + unsigned int blob_size; + + blob_size = offsetof(struct cirrus_amp_efi_data, data) + + sizeof(struct cirrus_amp_cal_data) * num_amps; + + priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob); + + priv->cal_blob->size = blob_size; + priv->cal_blob->count = num_amps; + + get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps); +} + +static u64 cs_amp_lib_test_get_target_uid(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + u64 uid; + + uid = priv->cal_blob->data[param->amp_index].calTarget[1]; + uid <<= 32; + uid |= priv->cal_blob->data[param->amp_index].calTarget[0]; + + return uid; +} + +/* Redirected get_efi_variable to simulate that the file is too short */ +static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + if (!buf) { + *size = offsetof(struct cirrus_amp_efi_data, data) - 1; + return EFI_BUFFER_TOO_SMALL; + } + + return EFI_NOT_FOUND; +} + +/* Should return -EOVERFLOW if the header is larger than the EFI data */ +static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_nohead); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the count is larger than the file */ +static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + if (!buf) { + /* + * Return a size that is shorter than required for the + * declared number of entries. + */ + *size = priv->cal_blob->size - 1; + return EFI_BUFFER_TOO_SMALL; + } + + memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1); + + return EFI_SUCCESS; +} + +/* Should return -EOVERFLOW if the entry count is larger than the EFI data */ +static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_bad_count); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate that the variable not found */ +static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + return EFI_NOT_FOUND; +} + +/* If EFI doesn't contain a cal data variable the result should be -ENOENT */ +static void cs_amp_lib_test_no_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_none); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* Redirected get_efi_variable to simulate reading a cal data blob */ +static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData"; + static const efi_guid_t expected_guid = + EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size); + + KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name)); + KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid)); + + if (!buf) { + *size = priv->cal_blob->size; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small"); + + memcpy(buf, priv->cal_blob, priv->cal_blob->size); + + return EFI_SUCCESS; +} + +/* Get cal data block for a given amp, matched by target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + target_uid = cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL); + KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32); + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index without checking target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* Get cal data block for a given amp index with checked target UID. */ +static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + target_uid = cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * Get cal data block for a given amp index with checked target UID. + * The UID does not match so the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + u64 target_uid; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + /* Get a target UID that won't match the entry */ + target_uid = ~cs_amp_lib_test_get_target_uid(test); + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * Get cal data block for a given amp, where the cal data does not + * specify calTarget so the lookup falls back to using the index + */ +static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + const struct cs_amp_lib_test_param *param = test->param_value; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, + param->amp_index, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); + + KUNIT_EXPECT_EQ(test, result_data.calTime[0], + priv->cal_blob->data[param->amp_index].calTime[0]); + KUNIT_EXPECT_EQ(test, result_data.calTime[1], + priv->cal_blob->data[param->amp_index].calTime[1]); + KUNIT_EXPECT_EQ(test, result_data.calAmbient, + priv->cal_blob->data[param->amp_index].calAmbient); + KUNIT_EXPECT_EQ(test, result_data.calStatus, + priv->cal_blob->data[param->amp_index].calStatus); + KUNIT_EXPECT_EQ(test, result_data.calR, + priv->cal_blob->data[param->amp_index].calR); +} + +/* + * If the target UID isn't present in the cal data, and there isn't an + * index to fall back do, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't present in the cal data, and the index is + * out of range, the result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + static const u64 bad_target_uid = 0xBADCA100BABABABAULL; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values != bad_target_uid */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL); + priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32); + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99, + &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the target UID isn't given, and the index is out of range, the + * result should be -ENOENT. + */ +static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* If neither the target UID or the index is given the result should be -ENOENT. */ +static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +/* + * If the UID is passed as 0 this must not match an entry with an + * unpopulated calTarget + */ +static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int i, ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 8); + + /* Make all the target values zero so they are ignored */ + for (i = 0; i < priv->cal_blob->count; ++i) { + priv->cal_blob->data[i].calTarget[0] = 0; + priv->cal_blob->data[i].calTarget[1] = 0; + } + + /* Redirect calls to get EFI data */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data); + KUNIT_EXPECT_EQ(test, ret, -ENOENT); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable); +} + +static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = { + .alg_id = 0x9f210, + .mem_region = WMFW_ADSP2_YM, + .ambient = "CAL_AMBIENT", + .calr = "CAL_R", + .status = "CAL_STATUS", + .checksum = "CAL_CHECKSUM", +}; + +static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp, + const struct cirrus_amp_cal_controls *controls, + const char *ctl_name, u32 val) +{ + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name); + KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls); + + entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry); + + INIT_LIST_HEAD(&entry->list); + strscpy(entry->name, ctl_name, sizeof(entry->name)); + entry->value = val; + + list_add_tail(&entry->list, &priv->ctl_write_list); + + return 0; +} + +static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cs_amp_lib_test_ctl_write_entry *entry; + struct cirrus_amp_cal_data data; + struct cs_dsp *dsp; + int ret; + + dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp); + dsp->dev = &priv->amp_pdev.dev; + + get_random_bytes(&data, sizeof(data)); + + /* Redirect calls to write firmware controls */ + kunit_activate_static_stub(test, + cs_amp_test_hooks->write_cal_coeff, + cs_amp_lib_test_write_cal_coeff); + + ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data); + KUNIT_EXPECT_EQ(test, ret, 0); + + kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff); + + KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4); + + /* Checksum control must be written last */ + entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum); + KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient); + KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr); + KUNIT_EXPECT_EQ(test, entry->value, data.calR); + list_del(&entry->list); + + entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list); + KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status); + KUNIT_EXPECT_EQ(test, entry->value, data.calStatus); +} + +static void cs_amp_lib_test_dev_release(struct device *dev) +{ +} + +static int cs_amp_lib_test_case_init(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + test->priv = priv; + INIT_LIST_HEAD(&priv->ctl_write_list); + + /* Create dummy amp driver dev */ + priv->amp_pdev.name = "cs_amp_lib_test_drv"; + priv->amp_pdev.id = -1; + priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release; + ret = platform_device_register(&priv->amp_pdev); + KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n"); + + return 0; +} + +static void cs_amp_lib_test_case_exit(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + + if (priv->amp_pdev.name) + platform_device_unregister(&priv->amp_pdev); +} + +static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = { + { .num_amps = 2, .amp_index = 0 }, + { .num_amps = 2, .amp_index = 1 }, + + { .num_amps = 3, .amp_index = 0 }, + { .num_amps = 3, .amp_index = 1 }, + { .num_amps = 3, .amp_index = 2 }, + + { .num_amps = 4, .amp_index = 0 }, + { .num_amps = 4, .amp_index = 1 }, + { .num_amps = 4, .amp_index = 2 }, + { .num_amps = 4, .amp_index = 3 }, + + { .num_amps = 5, .amp_index = 0 }, + { .num_amps = 5, .amp_index = 1 }, + { .num_amps = 5, .amp_index = 2 }, + { .num_amps = 5, .amp_index = 3 }, + { .num_amps = 5, .amp_index = 4 }, + + { .num_amps = 6, .amp_index = 0 }, + { .num_amps = 6, .amp_index = 1 }, + { .num_amps = 6, .amp_index = 2 }, + { .num_amps = 6, .amp_index = 3 }, + { .num_amps = 6, .amp_index = 4 }, + { .num_amps = 6, .amp_index = 5 }, + + { .num_amps = 8, .amp_index = 0 }, + { .num_amps = 8, .amp_index = 1 }, + { .num_amps = 8, .amp_index = 2 }, + { .num_amps = 8, .amp_index = 3 }, + { .num_amps = 8, .amp_index = 4 }, + { .num_amps = 8, .amp_index = 5 }, + { .num_amps = 8, .amp_index = 6 }, + { .num_amps = 8, .amp_index = 7 }, +}; + +static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param, + char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d", + param->num_amps, param->amp_index); +} + +KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases, + cs_amp_lib_test_get_cal_param_desc); + +static struct kunit_case cs_amp_lib_test_cases[] = { + /* Tests for getting calibration data from EFI */ + KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test), + KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test), + KUNIT_CASE(cs_amp_lib_test_no_cal_data_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test), + KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test, + cs_amp_lib_test_get_cal_gen_params), + KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test, + cs_amp_lib_test_get_cal_gen_params), + + /* Tests for writing calibration data */ + KUNIT_CASE(cs_amp_lib_test_write_cal_data_test), + + { } /* terminator */ +}; + +static struct kunit_suite cs_amp_lib_test_suite = { + .name = "snd-soc-cs-amp-lib-test", + .init = cs_amp_lib_test_case_init, + .exit = cs_amp_lib_test_case_exit, + .test_cases = cs_amp_lib_test_cases, +}; + +kunit_test_suite(cs_amp_lib_test_suite); + +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); +MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 4e2e5157a73f..01ef4db5407d 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -6,6 +6,7 @@ // Cirrus Logic International Semiconductor Ltd. #include +#include #include #include #include @@ -27,6 +28,8 @@ static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, __be32 beval = cpu_to_be32(val); int ret; + KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); + if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { mutex_lock(&dsp->pwr_lock); cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); @@ -84,7 +87,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, const struct cirrus_amp_cal_data *data) { - if (IS_REACHABLE(CONFIG_FW_CS_DSP)) + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) return _cs_amp_write_cal_coeffs(dsp, controls, data); else return -ENODEV; @@ -98,6 +101,8 @@ static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, { u32 attr; + KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf); + if (IS_ENABLED(CONFIG_EFI)) return efi.get_variable(name, guid, &attr, size, buf); @@ -250,13 +255,22 @@ static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, struct cirrus_amp_cal_data *out_data) { - if (IS_ENABLED(CONFIG_EFI)) + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); else return -ENOENT; } EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, SND_SOC_CS_AMP_LIB); +static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { + .get_efi_variable = cs_amp_get_efi_variable, + .write_cal_coeff = cs_amp_write_cal_coeff, +}; + +const struct cs_amp_test_hooks * const cs_amp_test_hooks = + PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); +EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, SND_SOC_CS_AMP_LIB); + MODULE_DESCRIPTION("Cirrus Logic amplifier library"); MODULE_AUTHOR("Richard Fitzgerald "); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 755bb9a44f52dcc386c8db84d7c5a0f94ee95640 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Mon, 4 Mar 2024 16:21:28 +0900 Subject: ASoC: soc-core.c: Prefer to return dai->driver->name in snd_soc_dai_name_get() ASoC machine driver can use snd_soc_{of_}get_dlc() (A) to get DAI name for dlc (snd_soc_dai_link_component). In this function call dlc->dai_name is parsed via snd_soc_dai_name_get() (B). (A) int snd_soc_get_dlc(...) { ... (B) dlc->dai_name = snd_soc_dai_name_get(dai); ... } (B) has a priority to return dai->name as dlc->dai_name. In most cases card can probe successfully. However it has an issue that ASoC tries to rebind card. Here is a simplified flow for example: | a) Card probes successfully at first | b) One of the component bound to this card is removed for some | reason the component->dev is released | c) That component is re-registered v d) ASoC calls snd_soc_try_rebind_card() a) points dlc->dai_name to dai->name. b) releases all resource of the old DAI. c) creates new DAI structure. In result d) can not use dlc->dai_name to add new created DAI. So it's reasonable that prefer to return dai->driver->name in snd_soc_dai_name_get() because dai->driver is a pre-defined global variable. Also update snd_soc_is_matching_dai() for alignment. Signed-off-by: Chancel Liu Link: https://msgid.link/r/20240304072128.2845432-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 507cd3015ff4..1e94edba12eb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -283,13 +283,13 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, /* see snd_soc_dai_name_get() */ - if (strcmp(dlc->dai_name, dai->name) == 0) - return 1; - if (dai->driver->name && strcmp(dlc->dai_name, dai->driver->name) == 0) return 1; + if (strcmp(dlc->dai_name, dai->name) == 0) + return 1; + if (dai->component->name && strcmp(dlc->dai_name, dai->component->name) == 0) return 1; @@ -300,12 +300,12 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) { /* see snd_soc_is_matching_dai() */ - if (dai->name) - return dai->name; - if (dai->driver->name) return dai->driver->name; + if (dai->name) + return dai->name; + if (dai->component->name) return dai->component->name; -- cgit v1.2.3 From 8fedf4f1d62ed058958e7a46aa62c0cb656dc040 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Mar 2024 18:07:22 +0200 Subject: ASoC: Intel: atom: sst_ipc: Remove unused intel-mid.h intel-mid.h is providing some core parts of the South Complex PM, which are usually are not used by individual drivers. In particular, this driver doesn't use it, so simply remove the unused header. Signed-off-by: Andy Shevchenko Link: https://msgid.link/r/20240305160723.1363534-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_ipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 3fc2c9a6c44d..0630e58b9d6b 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -19,8 +19,9 @@ #include #include #include -#include + #include + #include "../sst-mfld-platform.h" #include "sst.h" -- cgit v1.2.3 From 6ef46a69ec32fe1cf56de67742fcd01af4bf48af Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 6 Mar 2024 10:30:00 +0100 Subject: ASoC: trace: add component to set_bias_level trace events The snd_soc_bias_level_start and snd_soc_bias_level_done trace events currently look like: aplay-229 [000] 1250.140778: snd_soc_bias_level_start: card=vscn-2046 val=1 aplay-229 [000] 1250.140784: snd_soc_bias_level_done: card=vscn-2046 val=1 aplay-229 [000] 1250.140786: snd_soc_bias_level_start: card=vscn-2046 val=2 aplay-229 [000] 1250.140788: snd_soc_bias_level_done: card=vscn-2046 val=2 kworker/u8:1-21 [000] 1250.140871: snd_soc_bias_level_start: card=vscn-2046 val=1 kworker/u8:0-11 [000] 1250.140951: snd_soc_bias_level_start: card=vscn-2046 val=1 kworker/u8:0-11 [000] 1250.140956: snd_soc_bias_level_done: card=vscn-2046 val=1 kworker/u8:0-11 [000] 1250.140959: snd_soc_bias_level_start: card=vscn-2046 val=2 kworker/u8:0-11 [000] 1250.140961: snd_soc_bias_level_done: card=vscn-2046 val=2 kworker/u8:1-21 [000] 1250.167219: snd_soc_bias_level_done: card=vscn-2046 val=1 kworker/u8:1-21 [000] 1250.167222: snd_soc_bias_level_start: card=vscn-2046 val=2 kworker/u8:1-21 [000] 1250.167232: snd_soc_bias_level_done: card=vscn-2046 val=2 kworker/u8:0-11 [000] 1250.167440: snd_soc_bias_level_start: card=vscn-2046 val=3 kworker/u8:0-11 [000] 1250.167444: snd_soc_bias_level_done: card=vscn-2046 val=3 kworker/u8:1-21 [000] 1250.167497: snd_soc_bias_level_start: card=vscn-2046 val=3 kworker/u8:1-21 [000] 1250.167506: snd_soc_bias_level_done: card=vscn-2046 val=3 There are clearly multiple calls, one per component, but they cannot be discriminated from each other. Change the ftrace events to also print the component name, to make it clear which part of the code is involved. This requires changing the passed value from a struct snd_soc_card, where the DAPM context is not kwown, to a struct snd_soc_dapm_context where it is obviously known but the a card pointer is also available. With this change, the resulting trace becomes: aplay-247 [000] 1436.357332: snd_soc_bias_level_start: card=vscn-2046 component=(none) val=1 aplay-247 [000] 1436.357338: snd_soc_bias_level_done: card=vscn-2046 component=(none) val=1 aplay-247 [000] 1436.357340: snd_soc_bias_level_start: card=vscn-2046 component=(none) val=2 aplay-247 [000] 1436.357343: snd_soc_bias_level_done: card=vscn-2046 component=(none) val=2 kworker/u8:4-215 [000] 1436.357437: snd_soc_bias_level_start: card=vscn-2046 component=ff560000.codec val=1 kworker/u8:5-231 [000] 1436.357518: snd_soc_bias_level_start: card=vscn-2046 component=ff320000.i2s val=1 kworker/u8:5-231 [000] 1436.357523: snd_soc_bias_level_done: card=vscn-2046 component=ff320000.i2s val=1 kworker/u8:5-231 [000] 1436.357526: snd_soc_bias_level_start: card=vscn-2046 component=ff320000.i2s val=2 kworker/u8:5-231 [000] 1436.357528: snd_soc_bias_level_done: card=vscn-2046 component=ff320000.i2s val=2 kworker/u8:4-215 [000] 1436.383217: snd_soc_bias_level_done: card=vscn-2046 component=ff560000.codec val=1 kworker/u8:4-215 [000] 1436.383221: snd_soc_bias_level_start: card=vscn-2046 component=ff560000.codec val=2 kworker/u8:4-215 [000] 1436.383231: snd_soc_bias_level_done: card=vscn-2046 component=ff560000.codec val=2 kworker/u8:5-231 [000] 1436.383468: snd_soc_bias_level_start: card=vscn-2046 component=ff320000.i2s val=3 kworker/u8:5-231 [000] 1436.383472: snd_soc_bias_level_done: card=vscn-2046 component=ff320000.i2s val=3 kworker/u8:4-215 [000] 1436.383503: snd_soc_bias_level_start: card=vscn-2046 component=ff560000.codec val=3 kworker/u8:4-215 [000] 1436.383513: snd_soc_bias_level_done: card=vscn-2046 component=ff560000.codec val=3 Signed-off-by: Luca Ceresoli Link: https://msgid.link/r/20240306-improve-asoc-trace-events-v1-1-edb252bbeb10@bootlin.com Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 29 ++++++++++++++++------------- sound/soc/soc-dapm.c | 4 ++-- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'sound/soc') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 4d8ef71090af..b7ac7f100bb4 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -17,38 +17,41 @@ struct snd_soc_card; struct snd_soc_dapm_widget; struct snd_soc_dapm_path; -DECLARE_EVENT_CLASS(snd_soc_card, +DECLARE_EVENT_CLASS(snd_soc_dapm, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val), + TP_ARGS(dapm, val), TP_STRUCT__entry( - __string( name, card->name ) - __field( int, val ) + __string( card_name, dapm->card->name) + __string( comp_name, dapm->component ? dapm->component->name : "(none)") + __field( int, val) ), TP_fast_assign( - __assign_str(name, card->name); + __assign_str(card_name, dapm->card->name); + __assign_str(comp_name, dapm->component ? dapm->component->name : "(none)"); __entry->val = val; ), - TP_printk("card=%s val=%d", __get_str(name), (int)__entry->val) + TP_printk("card=%s component=%s val=%d", + __get_str(card_name), __get_str(comp_name), (int)__entry->val) ); -DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_start, +DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_start, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val) + TP_ARGS(dapm, val) ); -DEFINE_EVENT(snd_soc_card, snd_soc_bias_level_done, +DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_done, - TP_PROTO(struct snd_soc_card *card, int val), + TP_PROTO(struct snd_soc_dapm_context *dapm, int val), - TP_ARGS(card, val) + TP_ARGS(dapm, val) ); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bffeea80277f..7e8a2a5312f5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -725,7 +725,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, struct snd_soc_card *card = dapm->card; int ret = 0; - trace_snd_soc_bias_level_start(card, level); + trace_snd_soc_bias_level_start(dapm, level); ret = snd_soc_card_set_bias_level(card, dapm, level); if (ret != 0) @@ -739,7 +739,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, ret = snd_soc_card_set_bias_level_post(card, dapm, level); out: - trace_snd_soc_bias_level_done(card, level); + trace_snd_soc_bias_level_done(dapm, level); return ret; } -- cgit v1.2.3 From 7df3eb4cdb6bbfa482f51548b9fd47c2723c68ba Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 6 Mar 2024 10:30:01 +0100 Subject: ASoC: trace: add event to snd_soc_dapm trace events Add the event value to the snd_soc_dapm_start and snd_soc_dapm_done trace events to make them more informative. Trace before: aplay-229 [000] 250.140309: snd_soc_dapm_start: card=vscn-2046 aplay-229 [000] 250.167531: snd_soc_dapm_done: card=vscn-2046 aplay-229 [000] 251.169588: snd_soc_dapm_start: card=vscn-2046 aplay-229 [000] 251.195245: snd_soc_dapm_done: card=vscn-2046 Trace after: aplay-214 [000] 693.290612: snd_soc_dapm_start: card=vscn-2046 event=1 aplay-214 [000] 693.315508: snd_soc_dapm_done: card=vscn-2046 event=1 aplay-214 [000] 694.537349: snd_soc_dapm_start: card=vscn-2046 event=2 aplay-214 [000] 694.563241: snd_soc_dapm_done: card=vscn-2046 event=2 Signed-off-by: Luca Ceresoli Link: https://msgid.link/r/20240306-improve-asoc-trace-events-v1-2-edb252bbeb10@bootlin.com Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 16 +++++++++------- sound/soc/soc-dapm.c | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index b7ac7f100bb4..4eed9028bb11 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -57,34 +57,36 @@ DEFINE_EVENT(snd_soc_dapm, snd_soc_bias_level_done, DECLARE_EVENT_CLASS(snd_soc_dapm_basic, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card), + TP_ARGS(card, event), TP_STRUCT__entry( __string( name, card->name ) + __field( int, event ) ), TP_fast_assign( __assign_str(name, card->name); + __entry->event = event; ), - TP_printk("card=%s", __get_str(name)) + TP_printk("card=%s event=%d", __get_str(name), (int)__entry->event) ); DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_start, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card) + TP_ARGS(card, event) ); DEFINE_EVENT(snd_soc_dapm_basic, snd_soc_dapm_done, - TP_PROTO(struct snd_soc_card *card), + TP_PROTO(struct snd_soc_card *card, int event), - TP_ARGS(card) + TP_ARGS(card, event) ); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e8a2a5312f5..ad8ba8fbbaee 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1963,7 +1963,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) snd_soc_dapm_mutex_assert_held(card); - trace_snd_soc_dapm_start(card); + trace_snd_soc_dapm_start(card, event); for_each_card_dapms(card, d) { if (dapm_idle_bias_off(d)) @@ -2088,7 +2088,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) "DAPM sequencing finished, waiting %dms\n", card->pop_time); pop_wait(card->pop_time); - trace_snd_soc_dapm_done(card); + trace_snd_soc_dapm_done(card, event); return 0; } -- cgit v1.2.3 From bb6983847fb4535bb0386a91dd523088ece36450 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Thu, 7 Mar 2024 13:12:21 +0800 Subject: ASoC: codecs: ES8326: Changing members of private structure We don't use mic1_src and mic2_src.so we delete these two members. We changed the default value of interrupt-clk for headphone detection Signed-off-by: Zhang Yi Link: https://msgid.link/r/20240307051222.24010-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 608862aebd71..15289dadafea 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -31,8 +31,6 @@ struct es8326_priv { * while enabling or disabling or during an irq. */ struct mutex lock; - u8 mic1_src; - u8 mic2_src; u8 jack_pol; u8 interrupt_src; u8 interrupt_clk; @@ -1092,20 +1090,6 @@ static int es8326_probe(struct snd_soc_component *component) es8326->jd_inverted = device_property_read_bool(component->dev, "everest,jack-detect-inverted"); - ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src); - if (ret != 0) { - dev_dbg(component->dev, "mic1-src return %d", ret); - es8326->mic1_src = ES8326_ADC_AMIC; - } - dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src); - - ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src); - if (ret != 0) { - dev_dbg(component->dev, "mic2-src return %d", ret); - es8326->mic2_src = ES8326_ADC_DMIC; - } - dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src); - ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); if (ret != 0) { dev_dbg(component->dev, "jack-pol return %d", ret); @@ -1125,7 +1109,7 @@ static int es8326_probe(struct snd_soc_component *component) &es8326->interrupt_clk); if (ret != 0) { dev_dbg(component->dev, "interrupt-clk return %d", ret); - es8326->interrupt_clk = 0x45; + es8326->interrupt_clk = 0x00; } dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); -- cgit v1.2.3 From f193957b0fbbba397c8bddedf158b3bf7e4850fc Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 7 Mar 2024 11:02:27 +0000 Subject: ASoC: wm_adsp: Fix missing mutex_lock in wm_adsp_write_ctl() wm_adsp_write_ctl() must hold the pwr_lock mutex when calling cs_dsp_get_ctl(). This was previously partially fixed by commit 781118bc2fc1 ("ASoC: wm_adsp: Fix missing locking in wm_adsp_[read|write]_ctl()") but this only put locking around the call to cs_dsp_coeff_write_ctrl(), missing the call to cs_dsp_get_ctl(). Signed-off-by: Richard Fitzgerald Fixes: 781118bc2fc1 ("ASoC: wm_adsp: Fix missing locking in wm_adsp_[read|write]_ctl()") Link: https://msgid.link/r/20240307110227.41421-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 36ea0dcdc7ab..9cb9068c0462 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -683,11 +683,12 @@ static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { - struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + struct cs_dsp_coeff_ctl *cs_ctl; struct wm_coeff_ctl *ctl; int ret; mutex_lock(&dsp->cs_dsp.pwr_lock); + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); mutex_unlock(&dsp->cs_dsp.pwr_lock); -- cgit v1.2.3 From 6c023ad32b192dea51a4f842cc6ecf89bb6238c9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 Mar 2024 18:37:34 +0200 Subject: ASoC: Intel: catpt: Carefully use PCI bitwise constants PM constants for PCI devices are defined with bitwise annotation. When used as is, sparse complains about that: .../catpt/dsp.c:390:9: warning: restricted pci_power_t degrades to integer .../catpt/dsp.c:414:9: warning: restricted pci_power_t degrades to integer Force them to be u32 in the driver. Signed-off-by: Andy Shevchenko Acked-by: Cezary Rojewski Link: https://msgid.link/r/20240307163734.3852754-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/dsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index 346bec000306..5454c6d9ab5b 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -387,7 +387,7 @@ int catpt_dsp_power_down(struct catpt_dev *cdev) mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit; catpt_updatel_pci(cdev, VDRTCTL0, mask, cdev->spec->d3pgd_bit); - catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D3hot); + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot); /* give hw time to drop off */ udelay(50); @@ -411,7 +411,7 @@ int catpt_dsp_power_up(struct catpt_dev *cdev) val = mask & (~CATPT_VDRTCTL2_DTCGE); catpt_updatel_pci(cdev, VDRTCTL2, mask, val); - catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D0); + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0); /* SRAM power gating none */ mask = cdev->spec->d3srampgd_bit | cdev->spec->d3pgd_bit; -- cgit v1.2.3 From afd17e6debf9494af778a58ec7706da05ede0730 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Fri, 8 Mar 2024 13:58:58 +0000 Subject: ASoC: cs35l56: Add support for CS35L54 and CS35L57 The CS35L54 and CS35L57 are Boosted Smart Amplifiers. The CS35L54 has I2C/SPI control and I2S/TDM audio. The CS35L57 also has SoundWire control and audio. The hardware differences between L54, L56 and L57 do not affect the driver control interface so they can all be handled by the same driver. Signed-off-by: Simon Trimmer Signed-off-by: Richard Fitzgerald Acked-by: Mark Brown Signed-off-by: Takashi Iwai Message-ID: <20240308135900.603192-2-rf@opensource.cirrus.com> --- include/sound/cs35l56.h | 1 + sound/soc/codecs/cs35l56-sdw.c | 3 ++- sound/soc/codecs/cs35l56-shared.c | 8 ++++++-- sound/soc/codecs/cs35l56.c | 14 +++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index b24716ab2750..5fda814776b9 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -257,6 +257,7 @@ struct cs35l56_base { struct regmap *regmap; int irq; struct mutex irq_lock; + u8 type; u8 rev; bool init_done; bool fw_patched; diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index ab960a1c171e..abd296da6dbd 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -366,7 +366,7 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row); - if (cs35l56->base.rev < 0xb0) + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev < 0xb0)) return cs35l56_a1_kick_divider(cs35l56, peripheral); return 0; @@ -543,6 +543,7 @@ static const struct dev_pm_ops cs35l56_sdw_pm = { static const struct sdw_device_id cs35l56_sdw_id[] = { SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0), + SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0), {}, }; MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 995d979b6d87..aa550db02c16 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -692,13 +692,17 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) devid &= CS35L56_DEVID_MASK; switch (devid) { + case 0x35A54: case 0x35A56: + case 0x35A57: break; default: dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); return ret; } + cs35l56_base->type = devid & 0xFF; + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) { dev_err(cs35l56_base->dev, "Get Secure status failed\n"); @@ -719,8 +723,8 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) if (ret) return ret; - dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", - cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n", + cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid, fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing); /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 2c1313e34cce..beb51c87c6e9 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -972,6 +972,10 @@ static int cs35l56_component_probe(struct snd_soc_component *component) return -ENODEV; } + cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type); + if (!cs35l56->dsp.part) + return -ENOMEM; + cs35l56->component = component; wm_adsp2_component_probe(&cs35l56->dsp, component); @@ -1002,6 +1006,9 @@ static void cs35l56_component_remove(struct snd_soc_component *component) wm_adsp2_component_remove(&cs35l56->dsp, component); + kfree(cs35l56->dsp.part); + cs35l56->dsp.part = NULL; + kfree(cs35l56->dsp.fwf_name); cs35l56->dsp.fwf_name = NULL; @@ -1221,7 +1228,12 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) dsp = &cs35l56->dsp; cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); - dsp->part = "cs35l56"; + + /* + * dsp->part is filled in later as it is based on the DEVID. In a + * SoundWire system that cannot be read until enumeration has occurred + * and the device has attached. + */ dsp->fw = 12; dsp->wmfw_optional = true; -- cgit v1.2.3 From f31e0d0c2cad23e0cc48731634f85bb2d8707790 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 10 Mar 2024 15:38:51 +0100 Subject: ASoC: tlv320adc3xxx: Don't strip remove function when driver is builtin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using __exit for the remove function results in the remove callback being discarded with SND_SOC_TLV320ADC3XXX=y. When such a device gets unbound (e.g. using sysfs or hotplug), the driver is just removed without the cleanup being performed. This results in resource leaks. Fix it by compiling in the remove callback unconditionally. This also fixes a W=1 modpost warning: WARNING: modpost: sound/soc/codecs/snd-soc-tlv320adc3xxx: section mismatch in reference: adc3xxx_i2c_driver+0x10 (section: .data) -> adc3xxx_i2c_remove (section: .exit.text) (which only happens with SND_SOC_TLV320ADC3XXX=m). Fixes: e9a3b57efd28 ("ASoC: codec: tlv320adc3xxx: New codec driver") Signed-off-by: Uwe Kleine-König Reviewed-by: Geert Uytterhoeven Link: https://msgid.link/r/20240310143852.397212-2-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 420bbf588efe..e100cc9f5c19 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1429,7 +1429,7 @@ err_unprepare_mclk: return ret; } -static void __exit adc3xxx_i2c_remove(struct i2c_client *client) +static void adc3xxx_i2c_remove(struct i2c_client *client) { struct adc3xxx *adc3xxx = i2c_get_clientdata(client); @@ -1452,7 +1452,7 @@ static struct i2c_driver adc3xxx_i2c_driver = { .of_match_table = tlv320adc3xxx_of_match, }, .probe = adc3xxx_i2c_probe, - .remove = __exit_p(adc3xxx_i2c_remove), + .remove = adc3xxx_i2c_remove, .id_table = adc3xxx_i2c_id, }; -- cgit v1.2.3 From db185362fca554b201e2c62beb15a02bb39a064b Mon Sep 17 00:00:00 2001 From: M Cooley Date: Fri, 8 Mar 2024 17:35:40 -0500 Subject: ASoC: amd: yc: Fix non-functional mic on ASUS M7600RE The ASUS M7600RE (Vivobook Pro 16X OLED) needs a quirks-table entry for the internal microphone to function properly. Signed-off-by: Mitch Cooley Link: https://msgid.link/r/CALijGznExWW4fujNWwMzmn_K=wo96sGzV_2VkT7NjvEUdkg7Gw@mail.gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index cc231185d72c..384217c5eeeb 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -304,6 +304,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "E1504FA"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "M7600RE"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 9e2ab4b18ebd46813fc3459207335af4d368e323 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Tue, 5 Mar 2024 15:36:28 +0100 Subject: ASoC: rockchip: i2s-tdm: Fix inaccurate sampling rates The sample rates set by the rockchip_i2s_tdm driver in master mode are inaccurate up to 5% in several cases, due to the driver logic to configure clocks and a nasty interaction with the Common Clock Framework. To understand what happens, here is the relevant section of the clock tree (slightly simplified), along with the names used in the driver: vpll0 _OR_ vpll1 "mclk_root" clk_i2s2_8ch_tx_src "mclk_parent" clk_i2s2_8ch_tx_mux clk_i2s2_8ch_tx "mclk" or "mclk_tx" This is what happens when playing back e.g. at 192 kHz using audio-graph-card (when recording the same applies, only s/tx/rx/): 0. at probe, rockchip_i2s_tdm_set_sysclk() stores the passed frequency in i2s_tdm->mclk_tx_freq (*) which is 50176000, and that is never modified afterwards 1. when playback is started, rockchip_i2s_tdm_hw_params() is called and does the following two calls 2. rockchip_i2s_tdm_calibrate_mclk(): 2a. selects mclk_root0 (vpll0) as a parent for mclk_parent (mclk_tx_src), which is OK because the vpll0 rate is a good for 192000 (and sumbultiple) rates 2b. sets the mclk_root frequency based on ppm calibration computations 2c. sets mclk_tx_src to 49152000 (= 256 * 192000), which is also OK as it is a multiple of the required bit clock 3. rockchip_i2s_tdm_set_mclk() 3a. calls clk_set_rate() to set the rate of mclk_tx (clk_i2s2_8ch_tx) to the value of i2s_tdm->mclk_tx_freq (*), i.e. 50176000 which is not a multiple of the sampling frequency -- this is not OK 3a1. clk_set_rate() reacts by reparenting clk_i2s2_8ch_tx_src to vpll1 -- this is not OK because the default vpll1 rate can be divided to get 44.1 kHz and related rates, not 192 kHz The result is that the driver does a lot of ad-hoc decisions about clocks and ends up in using the wrong parent at an unoptimal rate. Step 0 is one part of the problem: unless the card driver calls set_sysclk at each stream start, whatever rate is set in mclk_tx_freq during boot will be taken and used until reboot. Moreover the driver does not care if its value is not a multiple of any audio frequency. Another part of the problem is that the whole reparenting and clock rate setting logic is conflicting with the CCF algorithms to achieve largely the same goal: selecting the best parent and setting the closest clock rate. And it turns out that only calling once clk_set_rate() on clk_i2s2_8ch_tx picks the correct vpll and sets the correct rate. The fix is based on removing the custom logic in the driver to select the parent and set the various clocks, and just let the Clock Framework do it all. As a side effect, the set_sysclk() op becomes useless because we now let the CCF compute the appropriate value for the sampling rate. It also implies that the whole calibration logic is now dead code and so it is removed along with the "PCM Clock Compensation in PPM" kcontrol, which has always been broken anyway. The handling of the 4 optional clocks also becomes dead code and is removed. The actual rates have been tested playing 30 seconds of audio at various sampling rates before and after this change using sox: time play -r -n synth 30 sine 950 gain -3 The time reported in the table below is the 'real' value reported by the 'time' command in the above command line. rate before after --------- ------ ------ 8000 Hz 30.60s 30.63s 11025 Hz 30.45s 30.51s 16000 Hz 30.47s 30.50s 22050 Hz 30.78s 30.41s 32000 Hz 31.02s 30.43s 44100 Hz 30.78s 30.41s 48000 Hz 29.81s 30.45s 88200 Hz 30.78s 30.41s 96000 Hz 29.79s 30.42s 176400 Hz 27.40s 30.41s 192000 Hz 29.79s 30.42s While the tests are running the clock tree confirms that: * without the patch, vpll1 is always used and clk_i2s2_8ch_tx always produces 50176000 Hz, which cannot be divided for most audio rates except the slowest ones, generating inaccurate rates * with the patch: - for 192000 Hz vpll0 is used - for 176400 Hz vpll1 is used - clk_i2s2_8ch_tx always produces (256 * ) Hz Tested on the RK3308 using the internal audio codec. Fixes: 081068fd6414 ("ASoC: rockchip: add support for i2s-tdm controller") Signed-off-by: Luca Ceresoli Link: https://msgid.link/r/20240305-rk3308-audio-codec-v4-1-312acdbe628f@bootlin.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s_tdm.c | 352 +--------------------------------- 1 file changed, 6 insertions(+), 346 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 860e66ec85e8..9fa020ef7eab 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -25,8 +25,6 @@ #define DEFAULT_MCLK_FS 256 #define CH_GRP_MAX 4 /* The max channel 8 / 2 */ #define MULTIPLEX_CH_MAX 10 -#define CLK_PPM_MIN -1000 -#define CLK_PPM_MAX 1000 #define TRCM_TXRX 0 #define TRCM_TX 1 @@ -53,20 +51,6 @@ struct rk_i2s_tdm_dev { struct clk *hclk; struct clk *mclk_tx; struct clk *mclk_rx; - /* The mclk_tx_src is parent of mclk_tx */ - struct clk *mclk_tx_src; - /* The mclk_rx_src is parent of mclk_rx */ - struct clk *mclk_rx_src; - /* - * The mclk_root0 and mclk_root1 are root parent and supplies for - * the different FS. - * - * e.g: - * mclk_root0 is VPLL0, used for FS=48000Hz - * mclk_root1 is VPLL1, used for FS=44100Hz - */ - struct clk *mclk_root0; - struct clk *mclk_root1; struct regmap *regmap; struct regmap *grf; struct snd_dmaengine_dai_dma_data capture_dma_data; @@ -76,19 +60,11 @@ struct rk_i2s_tdm_dev { const struct rk_i2s_soc_data *soc_data; bool is_master_mode; bool io_multiplex; - bool mclk_calibrate; bool tdm_mode; - unsigned int mclk_rx_freq; - unsigned int mclk_tx_freq; - unsigned int mclk_root0_freq; - unsigned int mclk_root1_freq; - unsigned int mclk_root0_initial_freq; - unsigned int mclk_root1_initial_freq; unsigned int frame_width; unsigned int clk_trcm; unsigned int i2s_sdis[CH_GRP_MAX]; unsigned int i2s_sdos[CH_GRP_MAX]; - int clk_ppm; int refcount; spinlock_t lock; /* xfer lock */ bool has_playback; @@ -114,12 +90,6 @@ static void i2s_tdm_disable_unprepare_mclk(struct rk_i2s_tdm_dev *i2s_tdm) { clk_disable_unprepare(i2s_tdm->mclk_tx); clk_disable_unprepare(i2s_tdm->mclk_rx); - if (i2s_tdm->mclk_calibrate) { - clk_disable_unprepare(i2s_tdm->mclk_tx_src); - clk_disable_unprepare(i2s_tdm->mclk_rx_src); - clk_disable_unprepare(i2s_tdm->mclk_root0); - clk_disable_unprepare(i2s_tdm->mclk_root1); - } } /** @@ -142,29 +112,9 @@ static int i2s_tdm_prepare_enable_mclk(struct rk_i2s_tdm_dev *i2s_tdm) ret = clk_prepare_enable(i2s_tdm->mclk_rx); if (ret) goto err_mclk_rx; - if (i2s_tdm->mclk_calibrate) { - ret = clk_prepare_enable(i2s_tdm->mclk_tx_src); - if (ret) - goto err_mclk_rx; - ret = clk_prepare_enable(i2s_tdm->mclk_rx_src); - if (ret) - goto err_mclk_rx_src; - ret = clk_prepare_enable(i2s_tdm->mclk_root0); - if (ret) - goto err_mclk_root0; - ret = clk_prepare_enable(i2s_tdm->mclk_root1); - if (ret) - goto err_mclk_root1; - } return 0; -err_mclk_root1: - clk_disable_unprepare(i2s_tdm->mclk_root0); -err_mclk_root0: - clk_disable_unprepare(i2s_tdm->mclk_rx_src); -err_mclk_rx_src: - clk_disable_unprepare(i2s_tdm->mclk_tx_src); err_mclk_rx: clk_disable_unprepare(i2s_tdm->mclk_tx); err_mclk_tx: @@ -564,159 +514,6 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, I2S_XFER_RXS_START); } -static int rockchip_i2s_tdm_clk_set_rate(struct rk_i2s_tdm_dev *i2s_tdm, - struct clk *clk, unsigned long rate, - int ppm) -{ - unsigned long rate_target; - int delta, ret; - - if (ppm == i2s_tdm->clk_ppm) - return 0; - - if (ppm < 0) - delta = -1; - else - delta = 1; - - delta *= (int)div64_u64((u64)rate * (u64)abs(ppm) + 500000, - 1000000); - - rate_target = rate + delta; - - if (!rate_target) - return -EINVAL; - - ret = clk_set_rate(clk, rate_target); - if (ret) - return ret; - - i2s_tdm->clk_ppm = ppm; - - return 0; -} - -static int rockchip_i2s_tdm_calibrate_mclk(struct rk_i2s_tdm_dev *i2s_tdm, - struct snd_pcm_substream *substream, - unsigned int lrck_freq) -{ - struct clk *mclk_root; - struct clk *mclk_parent; - unsigned int mclk_root_freq; - unsigned int mclk_root_initial_freq; - unsigned int mclk_parent_freq; - unsigned int div, delta; - u64 ppm; - int ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - mclk_parent = i2s_tdm->mclk_tx_src; - else - mclk_parent = i2s_tdm->mclk_rx_src; - - switch (lrck_freq) { - case 8000: - case 16000: - case 24000: - case 32000: - case 48000: - case 64000: - case 96000: - case 192000: - mclk_root = i2s_tdm->mclk_root0; - mclk_root_freq = i2s_tdm->mclk_root0_freq; - mclk_root_initial_freq = i2s_tdm->mclk_root0_initial_freq; - mclk_parent_freq = DEFAULT_MCLK_FS * 192000; - break; - case 11025: - case 22050: - case 44100: - case 88200: - case 176400: - mclk_root = i2s_tdm->mclk_root1; - mclk_root_freq = i2s_tdm->mclk_root1_freq; - mclk_root_initial_freq = i2s_tdm->mclk_root1_initial_freq; - mclk_parent_freq = DEFAULT_MCLK_FS * 176400; - break; - default: - dev_err(i2s_tdm->dev, "Invalid LRCK frequency: %u Hz\n", - lrck_freq); - return -EINVAL; - } - - ret = clk_set_parent(mclk_parent, mclk_root); - if (ret) - return ret; - - ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, mclk_root, - mclk_root_freq, 0); - if (ret) - return ret; - - delta = abs(mclk_root_freq % mclk_parent_freq - mclk_parent_freq); - ppm = div64_u64((uint64_t)delta * 1000000, (uint64_t)mclk_root_freq); - - if (ppm) { - div = DIV_ROUND_CLOSEST(mclk_root_initial_freq, mclk_parent_freq); - if (!div) - return -EINVAL; - - mclk_root_freq = mclk_parent_freq * round_up(div, 2); - - ret = clk_set_rate(mclk_root, mclk_root_freq); - if (ret) - return ret; - - i2s_tdm->mclk_root0_freq = clk_get_rate(i2s_tdm->mclk_root0); - i2s_tdm->mclk_root1_freq = clk_get_rate(i2s_tdm->mclk_root1); - } - - return clk_set_rate(mclk_parent, mclk_parent_freq); -} - -static int rockchip_i2s_tdm_set_mclk(struct rk_i2s_tdm_dev *i2s_tdm, - struct snd_pcm_substream *substream, - struct clk **mclk) -{ - unsigned int mclk_freq; - int ret; - - if (i2s_tdm->clk_trcm) { - if (i2s_tdm->mclk_tx_freq != i2s_tdm->mclk_rx_freq) { - dev_err(i2s_tdm->dev, - "clk_trcm, tx: %d and rx: %d should be the same\n", - i2s_tdm->mclk_tx_freq, - i2s_tdm->mclk_rx_freq); - return -EINVAL; - } - - ret = clk_set_rate(i2s_tdm->mclk_tx, i2s_tdm->mclk_tx_freq); - if (ret) - return ret; - - ret = clk_set_rate(i2s_tdm->mclk_rx, i2s_tdm->mclk_rx_freq); - if (ret) - return ret; - - /* mclk_rx is also ok. */ - *mclk = i2s_tdm->mclk_tx; - } else { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - *mclk = i2s_tdm->mclk_tx; - mclk_freq = i2s_tdm->mclk_tx_freq; - } else { - *mclk = i2s_tdm->mclk_rx; - mclk_freq = i2s_tdm->mclk_rx_freq; - } - - ret = clk_set_rate(*mclk, mclk_freq); - if (ret) - return ret; - } - - return 0; -} - static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture) { if (substream_capture) { @@ -853,19 +650,17 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); - struct clk *mclk; - int ret = 0; unsigned int val = 0; unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64; + int err; if (i2s_tdm->is_master_mode) { - if (i2s_tdm->mclk_calibrate) - rockchip_i2s_tdm_calibrate_mclk(i2s_tdm, substream, - params_rate(params)); + struct clk *mclk = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + i2s_tdm->mclk_tx : i2s_tdm->mclk_rx; - ret = rockchip_i2s_tdm_set_mclk(i2s_tdm, substream, &mclk); - if (ret) - return ret; + err = clk_set_rate(mclk, DEFAULT_MCLK_FS * params_rate(params)); + if (err) + return err; mclk_rate = clk_get_rate(mclk); bclk_rate = i2s_tdm->frame_width * params_rate(params); @@ -973,96 +768,6 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, return 0; } -static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, - unsigned int freq, int dir) -{ - struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); - - /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ - if (i2s_tdm->clk_trcm) { - i2s_tdm->mclk_tx_freq = freq; - i2s_tdm->mclk_rx_freq = freq; - } else { - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s_tdm->mclk_tx_freq = freq; - else - i2s_tdm->mclk_rx_freq = freq; - } - - dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", - stream ? "rx" : "tx", freq); - - return 0; -} - -static int rockchip_i2s_tdm_clk_compensation_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 = CLK_PPM_MIN; - uinfo->value.integer.max = CLK_PPM_MAX; - uinfo->value.integer.step = 1; - - return 0; -} - -static int rockchip_i2s_tdm_clk_compensation_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); - struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); - - ucontrol->value.integer.value[0] = i2s_tdm->clk_ppm; - - return 0; -} - -static int rockchip_i2s_tdm_clk_compensation_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); - struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); - int ret = 0, ppm = 0; - int changed = 0; - unsigned long old_rate; - - if (ucontrol->value.integer.value[0] < CLK_PPM_MIN || - ucontrol->value.integer.value[0] > CLK_PPM_MAX) - return -EINVAL; - - ppm = ucontrol->value.integer.value[0]; - - old_rate = clk_get_rate(i2s_tdm->mclk_root0); - ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root0, - i2s_tdm->mclk_root0_freq, ppm); - if (ret) - return ret; - if (old_rate != clk_get_rate(i2s_tdm->mclk_root0)) - changed = 1; - - if (clk_is_match(i2s_tdm->mclk_root0, i2s_tdm->mclk_root1)) - return changed; - - old_rate = clk_get_rate(i2s_tdm->mclk_root1); - ret = rockchip_i2s_tdm_clk_set_rate(i2s_tdm, i2s_tdm->mclk_root1, - i2s_tdm->mclk_root1_freq, ppm); - if (ret) - return ret; - if (old_rate != clk_get_rate(i2s_tdm->mclk_root1)) - changed = 1; - - return changed; -} - -static struct snd_kcontrol_new rockchip_i2s_tdm_compensation_control = { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "PCM Clock Compensation in PPM", - .info = rockchip_i2s_tdm_clk_compensation_info, - .get = rockchip_i2s_tdm_clk_compensation_get, - .put = rockchip_i2s_tdm_clk_compensation_put, -}; - static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) { struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); @@ -1072,9 +777,6 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) if (i2s_tdm->has_playback) snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data); - if (i2s_tdm->mclk_calibrate) - snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); - return 0; } @@ -1115,7 +817,6 @@ static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { .probe = rockchip_i2s_tdm_dai_probe, .hw_params = rockchip_i2s_tdm_hw_params, .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio, - .set_sysclk = rockchip_i2s_tdm_set_sysclk, .set_fmt = rockchip_i2s_tdm_set_fmt, .set_tdm_slot = rockchip_dai_tdm_slot, .trigger = rockchip_i2s_tdm_trigger, @@ -1444,35 +1145,6 @@ static void rockchip_i2s_tdm_path_config(struct rk_i2s_tdm_dev *i2s_tdm, rockchip_i2s_tdm_tx_path_config(i2s_tdm, num); } -static int rockchip_i2s_tdm_get_calibrate_mclks(struct rk_i2s_tdm_dev *i2s_tdm) -{ - int num_mclks = 0; - - i2s_tdm->mclk_tx_src = devm_clk_get(i2s_tdm->dev, "mclk_tx_src"); - if (!IS_ERR(i2s_tdm->mclk_tx_src)) - num_mclks++; - - i2s_tdm->mclk_rx_src = devm_clk_get(i2s_tdm->dev, "mclk_rx_src"); - if (!IS_ERR(i2s_tdm->mclk_rx_src)) - num_mclks++; - - i2s_tdm->mclk_root0 = devm_clk_get(i2s_tdm->dev, "mclk_root0"); - if (!IS_ERR(i2s_tdm->mclk_root0)) - num_mclks++; - - i2s_tdm->mclk_root1 = devm_clk_get(i2s_tdm->dev, "mclk_root1"); - if (!IS_ERR(i2s_tdm->mclk_root1)) - num_mclks++; - - if (num_mclks < 4 && num_mclks != 0) - return -ENOENT; - - if (num_mclks == 4) - i2s_tdm->mclk_calibrate = 1; - - return 0; -} - static int rockchip_i2s_tdm_path_prepare(struct rk_i2s_tdm_dev *i2s_tdm, struct device_node *np, bool is_rx_path) @@ -1610,11 +1282,6 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->io_multiplex = of_property_read_bool(node, "rockchip,io-multiplex"); - ret = rockchip_i2s_tdm_get_calibrate_mclks(i2s_tdm); - if (ret) - return dev_err_probe(i2s_tdm->dev, ret, - "mclk-calibrate clocks missing"); - regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) { return dev_err_probe(i2s_tdm->dev, PTR_ERR(regs), @@ -1667,13 +1334,6 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) goto err_disable_hclk; } - if (i2s_tdm->mclk_calibrate) { - i2s_tdm->mclk_root0_initial_freq = clk_get_rate(i2s_tdm->mclk_root0); - i2s_tdm->mclk_root1_initial_freq = clk_get_rate(i2s_tdm->mclk_root1); - i2s_tdm->mclk_root0_freq = i2s_tdm->mclk_root0_initial_freq; - i2s_tdm->mclk_root1_freq = i2s_tdm->mclk_root1_initial_freq; - } - pm_runtime_enable(&pdev->dev); regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK, -- cgit v1.2.3 From 23fb6bc2696119391ec3a92ccaffe50e567c515e Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Tue, 5 Mar 2024 15:56:06 +0900 Subject: ASoC: soc-core.c: Skip dummy codec when adding platforms When pcm_runtime is adding platform components it will scan all registered components. In case of DPCM FE/BE some DAI links will configure dummy platform. However both dummy codec and dummy platform are using "snd-soc-dummy" as component->name. Dummy codec should be skipped when adding platforms otherwise there'll be overflow and UBSAN complains. Reported-by: Zhipeng Wang Signed-off-by: Chancel Liu Link: https://msgid.link/r/20240305065606.3778642-1-chancel.liu@nxp.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1e94edba12eb..2ec13d1634b6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1219,6 +1219,9 @@ static int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!snd_soc_is_matching_component(platform, component)) continue; + if (snd_soc_component_is_dummy(component) && component->num_dai) + continue; + snd_soc_rtd_add_component(rtd, component); } } -- cgit v1.2.3 From 861b3415e4dee06cc00cd1754808a7827b9105bf Mon Sep 17 00:00:00 2001 From: Jiawei Wang Date: Wed, 13 Mar 2024 09:58:52 +0800 Subject: ASoC: amd: yc: Revert "Fix non-functional mic on Lenovo 21J2" This reverts commit ed00a6945dc32462c2d3744a3518d2316da66fcc, which added a quirk entry to enable the Yellow Carp (YC) driver for the Lenovo 21J2 laptop. Although the microphone functioned with the YC driver, it resulted in incorrect driver usage. The Lenovo 21J2 is not a Yellow Carp platform, but a Pink Sardine platform, which already has an upstreamed driver. The microphone on the Lenovo 21J2 operates correctly with the CONFIG_SND_SOC_AMD_PS flag enabled and does not require the quirk entry. So this patch removes the quirk entry. Thanks to Mukunda Vijendar [1] for pointing this out. Link: https://lore.kernel.org/linux-sound/023092e1-689c-4b00-b93f-4092c3724fb6@amd.com/ [1] Signed-off-by: Jiawei Wang Link: https://lore.kernel.org/linux-sound/023092e1-689c-4b00-b93f-4092c3724fb6@amd.com/ [1] Link: https://msgid.link/r/20240313015853.3573242-2-me@jwang.link Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 1ab69a53174e..69c68d8e7a6b 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -199,13 +199,6 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21HY"), } }, - { - .driver_data = &acp6x_card, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "21J2"), - } - }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 37bee1855d0e3b6dbeb8de71895f6f68cad137be Mon Sep 17 00:00:00 2001 From: Jiawei Wang Date: Wed, 13 Mar 2024 09:58:53 +0800 Subject: ASoC: amd: yc: Revert "add new YC platform variant (0x63) support" This reverts commit 316a784839b21b122e1761cdca54677bb19a47fa, that enabled Yellow Carp (YC) driver for PCI revision id 0x63. Mukunda Vijendar [1] points out that revision 0x63 is Pink Sardine platform, not Yellow Carp. The YC driver should not be enabled for this platform. This patch prevents the YC driver from being incorrectly enabled. Link: https://lore.kernel.org/linux-sound/023092e1-689c-4b00-b93f-4092c3724fb6@amd.com/ [1] Signed-off-by: Jiawei Wang Link: https://msgid.link/r/20240313015853.3573242-3-me@jwang.link Signed-off-by: Mark Brown --- sound/soc/amd/yc/pci-acp6x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c index 694b8e313902..7af6a349b1d4 100644 --- a/sound/soc/amd/yc/pci-acp6x.c +++ b/sound/soc/amd/yc/pci-acp6x.c @@ -162,7 +162,6 @@ static int snd_acp6x_probe(struct pci_dev *pci, /* Yellow Carp device check */ switch (pci->revision) { case 0x60: - case 0x63: case 0x6f: break; default: -- cgit v1.2.3 From 33c3d813330718c403a60d220f03fbece0f4fb5c Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 20 Feb 2024 22:16:03 +0200 Subject: ASoC: SOF: amd: Move signed_fw_image to struct acp_quirk_entry The signed_fw_image member of struct sof_amd_acp_desc is used to enable signed firmware support in the driver via the acp_sof_quirk_table. In preparation to support additional use cases of the quirk table (i.e. adding new flags), move signed_fw_image to a new struct acp_quirk_entry and update all references to it accordingly. No functional changes intended. Signed-off-by: Cristian Ciocaltea Link: https://msgid.link/r/20240220201623.438944-2-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp-loader.c | 2 +- sound/soc/sof/amd/acp.c | 47 ++++++++++++++++++++++-------------------- sound/soc/sof/amd/acp.h | 6 +++++- sound/soc/sof/amd/vangogh.c | 9 ++++++-- 4 files changed, 38 insertions(+), 26 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index d2d21478399e..aad904839b81 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -173,7 +173,7 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) adata = sdev->pdata->hw_pdata; - if (adata->signed_fw_image) + if (adata->quirks && adata->quirks->signed_fw_image) size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; else size_fw = adata->fw_bin_size; diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 9b3c26210db3..9d9197fa83ed 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -20,12 +20,14 @@ #include "acp.h" #include "acp-dsp-offset.h" -#define SECURED_FIRMWARE 1 - static bool enable_fw_debug; module_param(enable_fw_debug, bool, 0444); MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); +static struct acp_quirk_entry quirk_valve_galileo = { + .signed_fw_image = true, +}; + const struct dmi_system_id acp_sof_quirk_table[] = { { /* Steam Deck OLED device */ @@ -33,7 +35,7 @@ const struct dmi_system_id acp_sof_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Valve"), DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), }, - .driver_data = (void *)SECURED_FIRMWARE, + .driver_data = &quirk_valve_galileo, }, {} }; @@ -254,7 +256,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } - if (adata->signed_fw_image) + if (adata->quirks && adata->quirks->signed_fw_image) snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); @@ -738,26 +740,27 @@ skip_soundwire: sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; sdev->debug_box.size = BOX_SIZE_1024; - adata->signed_fw_image = false; dmi_id = dmi_first_match(acp_sof_quirk_table); - if (dmi_id && dmi_id->driver_data) { - adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "sof-%s-code.bin", - chip->name); - if (!adata->fw_code_bin) { - ret = -ENOMEM; - goto free_ipc_irq; + if (dmi_id) { + adata->quirks = dmi_id->driver_data; + + if (adata->quirks->signed_fw_image) { + adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-code.bin", + chip->name); + if (!adata->fw_code_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } + + adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, + "sof-%s-data.bin", + chip->name); + if (!adata->fw_data_bin) { + ret = -ENOMEM; + goto free_ipc_irq; + } } - - adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL, - "sof-%s-data.bin", - chip->name); - if (!adata->fw_data_bin) { - ret = -ENOMEM; - goto free_ipc_irq; - } - - adata->signed_fw_image = dmi_id->driver_data; } adata->enable_fw_debug = enable_fw_debug; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 947068da39b5..b648ed194b9f 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -207,6 +207,10 @@ struct sof_amd_acp_desc { u64 sdw_acpi_dev_addr; }; +struct acp_quirk_entry { + bool signed_fw_image; +}; + /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; @@ -236,7 +240,7 @@ struct acp_dev_data { u8 *data_buf; dma_addr_t sram_dma_addr; u8 *sram_data_buf; - bool signed_fw_image; + struct acp_quirk_entry *quirks; struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c index de15d21aa6d9..bc6ffdb5471a 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -143,6 +143,7 @@ EXPORT_SYMBOL_NS(sof_vangogh_ops, SND_SOC_SOF_AMD_COMMON); int sof_vangogh_ops_init(struct snd_sof_dev *sdev) { const struct dmi_system_id *dmi_id; + struct acp_quirk_entry *quirks; /* common defaults */ memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); @@ -151,8 +152,12 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); dmi_id = dmi_first_match(acp_sof_quirk_table); - if (dmi_id && dmi_id->driver_data) - sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + if (dmi_id) { + quirks = dmi_id->driver_data; + + if (quirks->signed_fw_image) + sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + } return 0; } -- cgit v1.2.3 From 094d11768f740f11483dad4efcd9bbcffa4ce146 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 20 Feb 2024 22:16:04 +0200 Subject: ASoC: SOF: amd: Skip IRAM/DRAM size modification for Steam Deck OLED The recent introduction of the ACP/PSP communication for IRAM/DRAM fence register modification breaks the audio support on Valve's Steam Deck OLED device. It causes IPC timeout errors when trying to load DSP topology during probing: 1707255557.688176 kernel: snd_sof_amd_vangogh 0000:04:00.5: ipc tx timed out for 0x30100000 (msg/reply size: 48/0) 1707255557.689035 kernel: snd_sof_amd_vangogh 0000:04:00.5: ------------[ IPC dump start ]------------ 1707255557.689421 kernel: snd_sof_amd_vangogh 0000:04:00.5: dsp_msg = 0x0 dsp_ack = 0x91d14f6f host_msg = 0x1 host_ack = 0xead0f1a4 irq_stat > 1707255557.689730 kernel: snd_sof_amd_vangogh 0000:04:00.5: ------------[ IPC dump end ]------------ 1707255557.690074 kernel: snd_sof_amd_vangogh 0000:04:00.5: ------------[ DSP dump start ]------------ 1707255557.690376 kernel: snd_sof_amd_vangogh 0000:04:00.5: IPC timeout 1707255557.690744 kernel: snd_sof_amd_vangogh 0000:04:00.5: fw_state: SOF_FW_BOOT_COMPLETE (7) 1707255557.691037 kernel: snd_sof_amd_vangogh 0000:04:00.5: invalid header size 0xdb43fe7. FW oops is bogus 1707255557.694824 kernel: snd_sof_amd_vangogh 0000:04:00.5: unexpected fault 0x6942d3b3 trace 0x6942d3b3 1707255557.695392 kernel: snd_sof_amd_vangogh 0000:04:00.5: ------------[ DSP dump end ]------------ 1707255557.695755 kernel: snd_sof_amd_vangogh 0000:04:00.5: Failed to setup widget PIPELINE.6.ACPHS1.IN 1707255557.696069 kernel: snd_sof_amd_vangogh 0000:04:00.5: error: tplg component load failed -110 1707255557.696374 kernel: snd_sof_amd_vangogh 0000:04:00.5: error: failed to load DSP topology -22 1707255557.697904 kernel: snd_sof_amd_vangogh 0000:04:00.5: ASoC: error at snd_soc_component_probe on 0000:04:00.5: -22 1707255557.698405 kernel: sof_mach nau8821-max: ASoC: failed to instantiate card -22 1707255557.701061 kernel: sof_mach nau8821-max: error -EINVAL: Failed to register card(sof-nau8821-max) 1707255557.701624 kernel: sof_mach: probe of nau8821-max failed with error -22 Introduce a new member skip_iram_dram_size_mod to struct acp_quirk_entry and use it to skip IRAM/DRAM size modification for Vangogh Galileo device. Fixes: 55d7bbe43346 ("ASoC: SOF: amd: Add acp-psp mailbox interface for iram-dram fence register modification") Signed-off-by: Cristian Ciocaltea Link: https://msgid.link/r/20240220201623.438944-3-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 3 ++- sound/soc/sof/amd/acp.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 9d9197fa83ed..be7dc1e02284 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -26,6 +26,7 @@ MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); static struct acp_quirk_entry quirk_valve_galileo = { .signed_fw_image = true, + .skip_iram_dram_size_mod = true, }; const struct dmi_system_id acp_sof_quirk_table[] = { @@ -280,7 +281,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } /* psp_send_cmd only required for vangogh platform (rev - 5) */ - if (desc->rev == 5) { + if (desc->rev == 5 && !(adata->quirks && adata->quirks->skip_iram_dram_size_mod)) { /* Modify IRAM and DRAM size */ ret = psp_send_cmd(adata, MBOX_ACP_IRAM_DRAM_FENCE_COMMAND | IRAM_DRAM_FENCE_2); if (ret) diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index b648ed194b9f..e229bb6b849d 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -209,6 +209,7 @@ struct sof_amd_acp_desc { struct acp_quirk_entry { bool signed_fw_image; + bool skip_iram_dram_size_mod; }; /* Common device data struct for ACP devices */ -- cgit v1.2.3 From 9a8b202f8cb7ebebc71f1f2a353a21c76d3063a8 Mon Sep 17 00:00:00 2001 From: Shalini Manjunatha Date: Wed, 6 Mar 2024 16:23:20 +0530 Subject: ASoC: soc-compress: Fix and add DPCM locking We find mising DPCM locking inside soc_compr_set_params_fe before calling dpcm_be_dai_hw_params() and dpcm_be_dai_prepare() which cause lockdep assert for DPCM lock not held in __soc_pcm_hw_params() and __soc_pcm_prepare() Signed-off-by: Shalini Manjunatha Link: https://msgid.link/r/d985beeafdd32316eb45f20811eb7926da7a796e.1709720380.git.quic_c_shalma@quicinc.com Signed-off-by: Mark Brown --- sound/soc/soc-compress.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index a38fee48ee00..e692aa3b8b22 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -385,11 +385,15 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + snd_soc_dpcm_mutex_lock(fe); ret = dpcm_be_dai_hw_params(fe, stream); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) goto out; + snd_soc_dpcm_mutex_lock(fe); ret = dpcm_be_dai_prepare(fe, stream); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) goto out; -- cgit v1.2.3 From 188ab4bfd29d7c91e35873a360a31e95a6ff0816 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Mon, 27 Nov 2023 14:06:57 +0200 Subject: ASoC: SOF: ipc4-topology: support NHLT device type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The endpoint in NHLT table for a SSP port could have the device type NHLT_DEVICE_BT or NHLT_DEVICE_I2S. Use intel_nhlt_ssp_device_type() function to retrieve the device type before querying the endpoint blob to make sure we are always using correct device type parameter. Signed-off-by: Brent Lu Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Takashi Iwai Message-ID: <20231127120657.19764-3-peter.ujfalusi@linux.intel.com> --- sound/soc/sof/ipc4-topology.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index da4a83afb87a..f28edd9830c1 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1356,6 +1356,7 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s int sample_rate, channel_count; int bit_depth, ret; u32 nhlt_type; + int dev_type = 0; /* convert to NHLT type */ switch (linktype) { @@ -1371,18 +1372,30 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s &bit_depth); if (ret < 0) return ret; + + /* + * We need to know the type of the external device attached to a SSP + * port to retrieve the blob from NHLT. However, device type is not + * specified in topology. + * Query the type for the port and then pass that information back + * to the blob lookup function. + */ + dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt, + dai_index); + if (dev_type < 0) + return dev_type; break; default: return 0; } - dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n", - dai_index, nhlt_type, dir); + dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d dev type %d\n", + dai_index, nhlt_type, dir, dev_type); /* find NHLT blob with matching params */ cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type, bit_depth, bit_depth, channel_count, sample_rate, - dir, 0); + dir, dev_type); if (!cfg) { dev_err(sdev->dev, -- cgit v1.2.3 From fb9f8125ed9d9b8e11f309a7dbfbe7b40de48fba Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:07:58 +0200 Subject: ASoC: SOF: Add dsp_max_burst_size_in_ms member to snd_sof_pcm_stream The dsp_max_burst_size_in_ms can be used to save the length of the maximum burst size in ms the host DMA will use. Platform code can place constraint using this to avoid user space requesting too small ALSA buffer which will result xruns. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9ea2ac5adac7..04e5cb2c70a7 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -322,6 +322,7 @@ struct snd_sof_pcm_stream { struct work_struct period_elapsed_work; struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */ bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ + unsigned int dsp_max_burst_size_in_ms; /* The maximum size of the host DMA burst in ms */ /* * flag to indicate that the DSP pipelines should be kept * active or not while suspending the stream -- cgit v1.2.3 From 842bb8b62cc6f3546d61eb63115b32ebc6dd4a87 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:07:59 +0200 Subject: ASoC: SOF: ipc4-topology: Save the DMA maximum burst size for PCMs When setting up the pcm widget, save the DSP buffer size (in ms) for platform code to place a constraint on playback. On playback the DMA will fill the buffer on start and if the period size is smaller it will immediately overrun. On capture the DMA will move data in 1ms bursts. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-topology.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index da4a83afb87a..bb4cf6dd1e18 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -412,8 +412,9 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_copier *ipc4_copier; + struct snd_sof_pcm *spcm; int node_type = 0; - int ret; + int ret, dir; ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); if (!ipc4_copier) @@ -447,6 +448,25 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) } dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type); + spcm = snd_sof_find_spcm_comp(scomp, swidget->comp_id, &dir); + if (!spcm) + goto skip_gtw_cfg; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + struct snd_sof_pcm_stream *sps = &spcm->stream[dir]; + + sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms, + SOF_COPIER_DEEP_BUFFER_TOKENS, + swidget->tuples, + swidget->num_tuples, sizeof(u32), 1); + /* Set default DMA buffer size if it is not specified in topology */ + if (!sps->dsp_max_burst_size_in_ms) + sps->dsp_max_burst_size_in_ms = SOF_IPC4_MIN_DMA_BUFFER_SIZE; + } else { + /* Capture data is copied from DSP to host in 1ms bursts */ + spcm->stream[dir].dsp_max_burst_size_in_ms = 1; + } + skip_gtw_cfg: ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); if (!ipc4_copier->gtw_attr) { -- cgit v1.2.3 From fe76d2e75a6da97edd2b4ec5cfb9efd541be087a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:00 +0200 Subject: ASoC: SOF: Intel: hda-pcm: Use dsp_max_burst_size_in_ms to place constraint If the PCM have the dsp_max_burst_size_in_ms set then place a constraint to limit the minimum buffer time to avoid xruns caused by DMA bursts spinning on the ALSA buffer. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-pcm.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 18f07364d219..69fefcd1abc5 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -259,6 +259,27 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32); + /* + * The dsp_max_burst_size_in_ms is the length of the maximum burst size + * of the host DMA in the ALSA buffer. + * + * On playback start the DMA will transfer dsp_max_burst_size_in_ms + * amount of data in one initial burst to fill up the host DMA buffer. + * Consequent DMA burst sizes are shorter and their length can vary. + * To make sure that userspace allocate large enough ALSA buffer we need + * to place a constraint on the buffer time. + * + * On capture the DMA will transfer 1ms chunks. + * + * Exact dsp_max_burst_size_in_ms constraint is racy, so set the + * constraint to a minimum of 2x dsp_max_burst_size_in_ms. + */ + if (spcm->stream[direction].dsp_max_burst_size_in_ms) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, + spcm->stream[direction].dsp_max_burst_size_in_ms * USEC_PER_MSEC * 2, + UINT_MAX); + /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; return 0; -- cgit v1.2.3 From 67b182bea08a8d1092b91b57aefdfe420fce1634 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:01 +0200 Subject: ASoC: SOF: Intel: hda: Implement get_stream_position (Linear Link Position) When the Linear Link Position is not available in firmware SRAM window we use the host accessible position registers to read it. The address of the PPLCLLPL/U registers depend on the number of streams (playback+capture). At probe time the pplc_addr is calculated for each stream and we can use it to read the LLP without the need of address re-calculation. Set the get_stream_position callback in sof_hda_common_ops for all platforms: The callback is used for IPC4 delay calculations only but the register is a generic HDA register, not tied to any specific IPC version. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-common-ops.c | 2 ++ sound/soc/sof/intel/hda-stream.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 3 +++ 3 files changed, 37 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 2b385cddc385..80a69599a8c3 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -57,6 +57,8 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, + .get_stream_position = hda_dsp_get_stream_llp, + /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index b387b1a69d7e..48ea187f7230 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1063,3 +1063,35 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, return pos; } + +/** + * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream + * @sdev: SOF device + * @component: ASoC component + * @substream: PCM substream + * + * Returns the raw Linear Link Position value + */ +u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + u32 llp_l, llp_u; + + /* + * The pplc_addr have been calculated during probe in + * hda_dsp_stream_init(): + * pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + + * SOF_HDA_PPLC_BASE + + * SOF_HDA_PPLC_MULTI * total_stream + + * SOF_HDA_PPLC_INTERVAL * stream_index + * + * Use this pre-calculated address to avoid repeated re-calculation. + */ + llp_l = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL); + llp_u = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU); + + return ((u64)llp_u << 32) | llp_l; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index b36eb7c78913..9d26cad785fe 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -662,6 +662,9 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, int direction, bool can_sleep); +u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); -- cgit v1.2.3 From 4374f698d7d9f849b66f3fa8f7a64f0bc1a53d7f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:02 +0200 Subject: ASoC: SOF: Intel: mtl/lnl: Use the generic get_stream_position callback Drop the MTL mtl_dsp_get_stream_hda_link_position() function and related defines since it can only work on platforms which have 19 streams because of the use of 0x948 as base offset for the LLP registers. The generic hda_dsp_get_stream_hda_link_position() takes the number of streams into consideration when reading the LLP registers for the stream and can handle different HDA configurations. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/lnl.c | 2 -- sound/soc/sof/intel/mtl.c | 14 -------------- sound/soc/sof/intel/mtl.h | 10 ---------- 3 files changed, 26 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index 7ae017a00184..d1c73d407e68 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -134,8 +134,6 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; } - sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; - /* dsp core get/put */ sof_lnl_ops.core_get = mtl_dsp_core_get; sof_lnl_ops.core_put = mtl_dsp_core_put; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index df05dc77b8d5..060c34988e90 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -626,18 +626,6 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } -u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct hdac_stream *hstream = substream->runtime->private_data; - u32 llp_l, llp_u; - - llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index)); - llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index)); - return ((u64)llp_u << 32) | llp_l; -} - int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; @@ -707,8 +695,6 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) sof_mtl_ops.core_get = mtl_dsp_core_get; sof_mtl_ops.core_put = mtl_dsp_core_put; - sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; - sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) return -ENOMEM; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index cc5a1f46fd09..ea8c1b83f712 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -6,12 +6,6 @@ * Copyright(c) 2020-2022 Intel Corporation. All rights reserved. */ -/* HDA Registers */ -#define MTL_PPLCLLPL_BASE 0x948 -#define MTL_PPLCLLPU_STRIDE 0x10 -#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE) -#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE) - /* DSP Registers */ #define MTL_HFDSSCS 0x1000 #define MTL_HFDSSCS_SPA_MASK BIT(16) @@ -103,9 +97,5 @@ int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); void mtl_ipc_dump(struct snd_sof_dev *sdev); -u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); - int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core); int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core); -- cgit v1.2.3 From ce2faa9a180c1984225689b6b1cb26045f8b7470 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:03 +0200 Subject: ASoC: SOF: Introduce a new callback pair to be used for PCM delay reporting For delay calculation we need two information: Number of bytes transferred between the DSP and host memory (ALSA buffer) Number of frames transferred between the DSP and external device (link/codec/DMIC/etc). The reason for the different units (bytes vs frames) on host and dai side is that the format on the dai side is decided by the firmware and might not be the same as on the host side, thus the expectation is that the counter reflects the number of frames. The kernel know the host side format and in there we have access to the DMA position which is in bytes. In a simplified way, the DSP caused delay is the difference between the two counters. The existing get_stream_position callback is defined to retrieve the frame counter on the DAI side but it's name is too generic to be intuitive and makes it hard to define a callback for the host side. This patch introduces a new set of callbacks to replace the get_stream_position and define the host side equivalent: get_dai_frame_counter get_host_byte_counter Subsequent patches will remove the old callback. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-7-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 24 ++++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 6cf21e829e07..d83cd771015c 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -533,6 +533,30 @@ static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev, return 0; } +static inline u64 +snd_sof_pcm_get_dai_frame_counter(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->get_dai_frame_counter) + return sof_ops(sdev)->get_dai_frame_counter(sdev, component, + substream); + + return 0; +} + +static inline u64 +snd_sof_pcm_get_host_byte_counter(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->get_host_byte_counter) + return sof_ops(sdev)->get_host_byte_counter(sdev, component, + substream); + + return 0; +} + /* machine driver */ static inline int snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d453a4ce3b21..91043f177dfa 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -270,6 +270,27 @@ struct snd_sof_dsp_ops { struct snd_soc_component *component, struct snd_pcm_substream *substream); /* optional */ + /* + * optional callback to retrieve the number of frames left/arrived from/to + * the DSP on the DAI side (link/codec/DMIC/etc). + * + * The callback is used when the firmware does not provide this information + * via the shared SRAM window and it can be retrieved by host. + */ + u64 (*get_dai_frame_counter)(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); /* optional */ + + /* + * Optional callback to retrieve the number of bytes left/arrived from/to + * the DSP on the host side (bytes between host ALSA buffer and DSP). + * + * The callback is needed for ALSA delay reporting. + */ + u64 (*get_host_byte_counter)(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); /* optional */ + /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, -- cgit v1.2.3 From fd6f6a0632bc891673490bf4a92304172251825c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:04 +0200 Subject: ASoC: SOF: Intel: Set the dai/host get frame/byte counter callbacks Add implementation for reading the LDP (Linear DMA Position) to be used as get_host_byte_counter(). The LDP is counting the number of bytes moved between the DSP and host memory. Set the get_dai_frame_counter to hda_dsp_get_stream_llp, which is counting the frames on the link side of the DSP. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-8-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-common-ops.c | 2 ++ sound/soc/sof/intel/hda-stream.c | 31 +++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 3 +++ 3 files changed, 36 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 80a69599a8c3..4d7ea18604ee 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -58,6 +58,8 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { .pcm_ack = hda_dsp_pcm_ack, .get_stream_position = hda_dsp_get_stream_llp, + .get_dai_frame_counter = hda_dsp_get_stream_llp, + .get_host_byte_counter = hda_dsp_get_stream_ldp, /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 48ea187f7230..8504a4f27b60 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1095,3 +1095,34 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, return ((u64)llp_u << 32) | llp_l; } + +/** + * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream + * @sdev: SOF device + * @component: ASoC component + * @substream: PCM substream + * + * Returns the raw Linear Link Position value + */ +u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + u32 ldp_l, ldp_u; + + /* + * The pphc_addr have been calculated during probe in + * hda_dsp_stream_init(): + * pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + + * SOF_HDA_PPHC_BASE + + * SOF_HDA_PPHC_INTERVAL * stream_index + * + * Use this pre-calculated address to avoid repeated re-calculation. + */ + ldp_l = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPL); + ldp_u = readl(hext_stream->pphc_addr + AZX_REG_PPHCLDPU); + + return ((u64)ldp_u << 32) | ldp_l; +} diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 9d26cad785fe..81a1d4606d3c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -665,6 +665,9 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, struct snd_soc_component *component, struct snd_pcm_substream *substream); +u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags); -- cgit v1.2.3 From 37679a1bd372c8308a3faccf3438c9df642565b3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:05 +0200 Subject: ASoC: SOF: ipc4-pcm: Use the snd_sof_pcm_get_dai_frame_counter() for pcm_delay Switch to the new callback to retrieve the DAI (link) frame counter. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-9-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 0f332c8cdbe6..d0795f77cc15 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -897,11 +897,12 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, } /* - * HDaudio links don't support the LLP counter reported by firmware - * the link position is read directly from hardware registers. + * If the LLP counter is not reported by firmware in the SRAM window + * then read the dai (link) position via host accessible means if + * available. */ if (!time_info->llp_offset) { - tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream); + tmp_ptr = snd_sof_pcm_get_dai_frame_counter(sdev, component, substream); if (!tmp_ptr) return 0; } else { -- cgit v1.2.3 From 4ab6c38c664442c1fc9911eb3c5c6953d3dbcca5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:06 +0200 Subject: ASoC: SOF: Intel: hda-common-ops: Do not set the get_stream_position callback The get_stream_position has been replaced by get_dai_frame_counter, it should not be set to allow it to be dropped from core code. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-10-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-common-ops.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 4d7ea18604ee..d71bb66b9991 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -57,7 +57,6 @@ struct snd_sof_dsp_ops sof_hda_common_ops = { .pcm_pointer = hda_dsp_pcm_pointer, .pcm_ack = hda_dsp_pcm_ack, - .get_stream_position = hda_dsp_get_stream_llp, .get_dai_frame_counter = hda_dsp_get_stream_llp, .get_host_byte_counter = hda_dsp_get_stream_ldp, -- cgit v1.2.3 From 07007b8ac42cffc23043d00e56b0f67a75dc4b22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:07 +0200 Subject: ASoC: SOF: Remove the get_stream_position callback The get_stream_position has been replaced by get_dai_frame_counter and all related code can be dropped form the core. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-11-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ops.h | 10 ---------- sound/soc/sof/sof-priv.h | 9 --------- 2 files changed, 19 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index d83cd771015c..3cd748e13460 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -523,16 +523,6 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, return 0; } -static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position) - return sof_ops(sdev)->get_stream_position(sdev, component, substream); - - return 0; -} - static inline u64 snd_sof_pcm_get_dai_frame_counter(struct snd_sof_dev *sdev, struct snd_soc_component *component, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 91043f177dfa..d3c436f82604 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -261,15 +261,6 @@ struct snd_sof_dsp_ops { /* pcm ack */ int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ - /* - * optional callback to retrieve the link DMA position for the substream - * when the position is not reported in the shared SRAM windows but - * instead from a host-accessible hardware counter. - */ - u64 (*get_stream_position)(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - /* * optional callback to retrieve the number of frames left/arrived from/to * the DSP on the DAI side (link/codec/DMIC/etc). -- cgit v1.2.3 From 31d2874d083ba6cc2a4f4b26dab73c3be1c92658 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:08 +0200 Subject: ASoC: SOF: ipc4-pcm: Move struct sof_ipc4_timestamp_info definition locally The sof_ipc4_timestamp_info is only used by ipc4-pcm.c internally, it should not be in a generic header implying that it might be used elsewhere. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-12-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 14 ++++++++++++++ sound/soc/sof/ipc4-priv.h | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index d0795f77cc15..2d7295221884 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -15,6 +15,20 @@ #include "ipc4-topology.h" #include "ipc4-fw-reg.h" +/** + * struct sof_ipc4_timestamp_info - IPC4 timestamp info + * @host_copier: the host copier of the pcm stream + * @dai_copier: the dai copier of the pcm stream + * @stream_start_offset: reported by fw in memory window + * @llp_offset: llp offset in memory window + */ +struct sof_ipc4_timestamp_info { + struct sof_ipc4_copier *host_copier; + struct sof_ipc4_copier *dai_copier; + u64 stream_start_offset; + u32 llp_offset; +}; + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index f3b908b093f9..afed618a15f0 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -92,20 +92,6 @@ struct sof_ipc4_fw_data { struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ }; -/** - * struct sof_ipc4_timestamp_info - IPC4 timestamp info - * @host_copier: the host copier of the pcm stream - * @dai_copier: the dai copier of the pcm stream - * @stream_start_offset: reported by fw in memory window - * @llp_offset: llp offset in memory window - */ -struct sof_ipc4_timestamp_info { - struct sof_ipc4_copier *host_copier; - struct sof_ipc4_copier *dai_copier; - u64 stream_start_offset; - u32 llp_offset; -}; - extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; -- cgit v1.2.3 From 55ca6ca227bfc5a8d0a0c2c5d6e239777226a604 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:09 +0200 Subject: ASoC: SOF: ipc4-pcm: Combine the SOF_IPC4_PIPE_PAUSED cases in pcm_trigger The SNDRV_PCM_TRIGGER_PAUSE_PUSH does not need to be a separate case, it can be handled along with STOP and SUSPEND Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-13-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 2d7295221884..4e41b16d3205 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -478,14 +478,12 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component, /* determine the pipeline state */ switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - state = SOF_IPC4_PIPE_PAUSED; - break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: state = SOF_IPC4_PIPE_RUNNING; break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: state = SOF_IPC4_PIPE_PAUSED; -- cgit v1.2.3 From 3ce3bc36d91510389955b47e36ea4c4e94fcbdd3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:10 +0200 Subject: ASoC: SOF: ipc4-pcm: Invalidate the stream_start_offset in PAUSED state When the final state is SOF_IPC4_PIPE_PAUSED, it is possible that the stream will be restarted (resume or start) in which case we need to update the offset from the firmware. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-14-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 4e41b16d3205..905dbc4852b1 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -437,8 +437,19 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, } /* return if this is the final state */ - if (state == SOF_IPC4_PIPE_PAUSED) + if (state == SOF_IPC4_PIPE_PAUSED) { + struct sof_ipc4_timestamp_info *time_info; + + /* + * Invalidate the stream_start_offset to make sure that it is + * going to be updated if the stream resumes + */ + time_info = spcm->stream[substream->stream].private; + if (time_info) + time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; + goto free; + } skip_pause_transition: /* else set the RUNNING/RESET state in the DSP */ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); -- cgit v1.2.3 From 77165bd955d55114028b06787a530b8f9220e4b0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:11 +0200 Subject: ASoC: SOF: sof-pcm: Add pointer callback to sof_ipc_pcm_ops The IPC specific pointer callback can be used when additional or custom handling is needed during the pointer calculation, like executing a delay calculation at the same time to minimize drift between the reported pointer and the calculated delay. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-15-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pcm.c | 8 ++++++++ sound/soc/sof/sof-audio.h | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 33d576b17647..f03cee94bce6 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -388,13 +388,21 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; + int ret = -EOPNOTSUPP; /* nothing to do for BE */ if (rtd->dai_link->no_pcm) return 0; + if (pcm_ops && pcm_ops->pointer) + ret = pcm_ops->pointer(component, substream, &host); + + if (ret != -EOPNOTSUPP) + return ret ? ret : host; + /* use dsp ops pointer callback directly if set */ if (sof_ops(sdev)->pcm_pointer) return sof_ops(sdev)->pcm_pointer(sdev, substream); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 04e5cb2c70a7..86bbb531e142 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -103,7 +103,10 @@ struct snd_sof_dai_config_data { * additional memory in the SOF PCM stream structure * @pcm_free: Function pointer for PCM free that can be used for freeing any * additional memory in the SOF PCM stream structure - * @delay: Function pointer for pcm delay calculation + * @pointer: Function pointer for pcm pointer + * Note: the @pointer callback may return -EOPNOTSUPP which should be + * handled in a same way as if the callback is not provided + * @delay: Function pointer for pcm delay reporting * @reset_hw_params_during_stop: Flag indicating whether the hw_params should be reset during the * STOP pcm trigger * @ipc_first_on_start: Send IPC before invoking platform trigger during @@ -124,6 +127,9 @@ struct sof_ipc_pcm_ops { int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); + int (*pointer)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + snd_pcm_uframes_t *pointer); snd_pcm_sframes_t (*delay)(struct snd_soc_component *component, struct snd_pcm_substream *substream); bool reset_hw_params_during_stop; -- cgit v1.2.3 From 0ea06680dfcb4464ac6c05968433d060efb44345 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:12 +0200 Subject: ASoC: SOF: ipc4-pcm: Correct the delay calculation This patch improves the delay calculation by relying on the LLP (Linear Link Position) on the DAI side and the LDP (Linear Data Pointer) on the host side. The LDP provides the same DMA position as LPIB, but with a linear count instead of a position in the ALSA ring buffer. The LDP values are provided in bytes and must be converted to frames. The difference in units means that the host counter will wrap earlier than the LLP. We need to wrap the LLP at the same boundary as the host counter. The ASoC framework relies on separate pointer and delay callback. Measurement errors can be reduced by processing all the counter values in the pointer callback. The delay value is stored, and will be reported to higher levels in the delay callback. For playback, the firmware provides a stream_start offset to handle mixing/pause usages, where the DAI might have started earlier than the PCM device. The delay calculation must be special-cased when the link counter has not reached the start offset value, i.e. no valid audio has left the DSP. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-16-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 159 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 127 insertions(+), 32 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 905dbc4852b1..e915f9f87a6c 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -19,14 +19,22 @@ * struct sof_ipc4_timestamp_info - IPC4 timestamp info * @host_copier: the host copier of the pcm stream * @dai_copier: the dai copier of the pcm stream - * @stream_start_offset: reported by fw in memory window + * @stream_start_offset: reported by fw in memory window (converted to frames) + * @stream_end_offset: reported by fw in memory window (converted to frames) * @llp_offset: llp offset in memory window + * @boundary: wrap boundary should be used for the LLP frame counter + * @delay: Calculated and stored in pointer callback. The stored value is + * returned in the delay callback. */ struct sof_ipc4_timestamp_info { struct sof_ipc4_copier *host_copier; struct sof_ipc4_copier *dai_copier; u64 stream_start_offset; + u64 stream_end_offset; u32 llp_offset; + + u64 boundary; + snd_pcm_sframes_t delay; }; static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, @@ -726,6 +734,10 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm if (abi_version < SOF_IPC4_FW_REGS_ABI_VER) support_info = false; + /* For delay reporting the get_host_byte_counter callback is needed */ + if (!sof_ops(sdev) || !sof_ops(sdev)->get_host_byte_counter) + support_info = false; + for_each_pcm_streams(stream) { pipeline_list = &spcm->stream[stream].pipeline_list; @@ -858,7 +870,6 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, struct sof_ipc4_copier *host_copier = time_info->host_copier; struct sof_ipc4_copier *dai_copier = time_info->dai_copier; struct sof_ipc4_pipeline_registers ppl_reg; - u64 stream_start_position; u32 dai_sample_size; u32 ch, node_index; u32 offset; @@ -875,38 +886,51 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) return -EINVAL; - stream_start_position = ppl_reg.stream_start_offset; ch = dai_copier->data.out_format.fmt_cfg; ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch); dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch; - /* convert offset to sample count */ - do_div(stream_start_position, dai_sample_size); - time_info->stream_start_offset = stream_start_position; + + /* convert offsets to frame count */ + time_info->stream_start_offset = ppl_reg.stream_start_offset; + do_div(time_info->stream_start_offset, dai_sample_size); + time_info->stream_end_offset = ppl_reg.stream_end_offset; + do_div(time_info->stream_end_offset, dai_sample_size); + + /* + * Calculate the wrap boundary need to be used for delay calculation + * The host counter is in bytes, it will wrap earlier than the frames + * based link counter. + */ + time_info->boundary = div64_u64(~((u64)0), + frames_to_bytes(substream->runtime, 1)); + /* Initialize the delay value to 0 (no delay) */ + time_info->delay = 0; return 0; } -static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + snd_pcm_uframes_t *pointer) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; - snd_pcm_uframes_t head_ptr, tail_ptr; + snd_pcm_uframes_t head_cnt, tail_cnt; struct snd_sof_pcm_stream *stream; + u64 dai_cnt, host_cnt, host_ptr; struct snd_sof_pcm *spcm; - u64 tmp_ptr; int ret; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) - return 0; + return -EOPNOTSUPP; stream = &spcm->stream[substream->stream]; time_info = stream->private; if (!time_info) - return 0; + return -EOPNOTSUPP; /* * stream_start_offset is updated to memory window by FW based on @@ -916,46 +940,116 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info); if (ret < 0) - return 0; + return -EOPNOTSUPP; } + /* For delay calculation we need the host counter */ + host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream); + host_ptr = host_cnt; + + /* convert the host_cnt to frames */ + host_cnt = div64_u64(host_cnt, frames_to_bytes(substream->runtime, 1)); + /* * If the LLP counter is not reported by firmware in the SRAM window - * then read the dai (link) position via host accessible means if + * then read the dai (link) counter via host accessible means if * available. */ if (!time_info->llp_offset) { - tmp_ptr = snd_sof_pcm_get_dai_frame_counter(sdev, component, substream); - if (!tmp_ptr) - return 0; + dai_cnt = snd_sof_pcm_get_dai_frame_counter(sdev, component, substream); + if (!dai_cnt) + return -EOPNOTSUPP; } else { sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp)); - tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; + dai_cnt = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l; } + dai_cnt += time_info->stream_end_offset; - /* In two cases dai dma position is not accurate + /* In two cases dai dma counter is not accurate * (1) dai pipeline is started before host pipeline - * (2) multiple streams mixed into one. Each stream has the same dai dma position + * (2) multiple streams mixed into one. Each stream has the same dai dma + * counter + * + * Firmware calculates correct stream_start_offset for all cases + * including above two. + * Driver subtracts stream_start_offset from dai dma counter to get + * accurate one + */ + + /* + * On stream start the dai counter might not yet have reached the + * stream_start_offset value which means that no frames have left the + * DSP yet from the audio stream (on playback, capture streams have + * offset of 0 as we start capturing right away). + * In this case we need to adjust the distance between the counters by + * increasing the host counter by (offset - dai_counter). + * Otherwise the dai_counter needs to be adjusted to reflect the number + * of valid frames passed on the DAI side. * - * Firmware calculates correct stream_start_offset for all cases including above two. - * Driver subtracts stream_start_offset from dai dma position to get accurate one + * The delay is the difference between the counters on the two + * sides of the DSP. */ - tmp_ptr -= time_info->stream_start_offset; + if (dai_cnt < time_info->stream_start_offset) { + host_cnt += time_info->stream_start_offset - dai_cnt; + dai_cnt = 0; + } else { + dai_cnt -= time_info->stream_start_offset; + } + + /* Wrap the dai counter at the boundary where the host counter wraps */ + div64_u64_rem(dai_cnt, time_info->boundary, &dai_cnt); - /* Calculate the delay taking into account that both pointer can wrap */ - div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - head_ptr = substream->runtime->status->hw_ptr; - tail_ptr = tmp_ptr; + head_cnt = host_cnt; + tail_cnt = dai_cnt; } else { - head_ptr = tmp_ptr; - tail_ptr = substream->runtime->status->hw_ptr; + head_cnt = dai_cnt; + tail_cnt = host_cnt; + } + + if (head_cnt < tail_cnt) { + time_info->delay = time_info->boundary - tail_cnt + head_cnt; + goto out; } - if (head_ptr < tail_ptr) - return substream->runtime->boundary - tail_ptr + head_ptr; + time_info->delay = head_cnt - tail_cnt; + +out: + /* + * Convert the host byte counter to PCM pointer which wraps in buffer + * and it is in frames + */ + div64_u64_rem(host_ptr, snd_pcm_lib_buffer_bytes(substream), &host_ptr); + *pointer = bytes_to_frames(substream->runtime, host_ptr); + + return 0; +} + +static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_ipc4_timestamp_info *time_info; + struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return 0; + + stream = &spcm->stream[substream->stream]; + time_info = stream->private; + /* + * Report the stored delay value calculated in the pointer callback. + * In the unlikely event that the calculation was skipped/aborted, the + * default 0 delay returned. + */ + if (time_info) + return time_info->delay; + + /* No delay information available, report 0 as delay */ + return 0; - return head_ptr - tail_ptr; } const struct sof_ipc_pcm_ops ipc4_pcm_ops = { @@ -965,6 +1059,7 @@ const struct sof_ipc_pcm_ops ipc4_pcm_ops = { .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup, .pcm_setup = sof_ipc4_pcm_setup, .pcm_free = sof_ipc4_pcm_free, + .pointer = sof_ipc4_pcm_pointer, .delay = sof_ipc4_pcm_delay, .ipc_first_on_start = true, .platform_stop_during_hw_free = true, -- cgit v1.2.3 From 1abc2642588e06f6180b3fbb21968cf5d0ba9e5f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 21 Mar 2024 15:08:14 +0200 Subject: ASoC: SOF: Intel: hda: Compensate LLP in case it is not reset During pause/reset or stop/start the LLP counter is not reset, which will result broken delay reporting. Read the LLP value on STOP/PAUSE trigger and use it in LLP reading to normalize the LLP from the register. Cc: stable@vger.kernel.org # 6.8 Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240321130814.4412-18-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dai-ops.c | 11 +++++++++++ sound/soc/sof/intel/hda-pcm.c | 8 ++++++++ sound/soc/sof/intel/hda-stream.c | 9 ++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index c50ca9e72d37..b073720b4cf4 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -362,6 +363,16 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_stream_clear(hext_stream); + + /* + * Save the LLP registers in case the stream is + * restarting due PAUSE_RELEASE, or START without a pcm + * close/open since in this case the LLP register is not reset + * to 0 and the delay calculation will return with invalid + * results. + */ + hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL); + hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU); break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 69fefcd1abc5..d7b446f3f973 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -282,6 +282,14 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, /* binding pcm substream to hda stream */ substream->runtime->private_data = &dsp_stream->hstream; + + /* + * Reset the llp cache values (they are used for LLP compensation in + * case the counter is not reset) + */ + dsp_stream->pplcllpl = 0; + dsp_stream->pplcllpu = 0; + return 0; } diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 8504a4f27b60..0c189d3b19c1 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1064,6 +1064,8 @@ snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, return pos; } +#define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l)) + /** * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream * @sdev: SOF device @@ -1093,7 +1095,12 @@ u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, llp_l = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL); llp_u = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU); - return ((u64)llp_u << 32) | llp_l; + /* Compensate the LLP counter with the saved offset */ + if (hext_stream->pplcllpl || hext_stream->pplcllpu) + return merge_u64(llp_u, llp_l) - + merge_u64(hext_stream->pplcllpu, hext_stream->pplcllpl); + + return merge_u64(llp_u, llp_l); } /** -- cgit v1.2.3 From c61115b37ff964d63191dbf4a058f481daabdf57 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 22 Mar 2024 13:25:04 +0200 Subject: ASoC: SOF: Intel: hda-dsp: Skip IMR boot on ACE platforms in case of S3 suspend SoCs with ACE architecture are tailored to use s2idle instead deep (S3) suspend state and the IMR content is lost when the system is forced to enter even to S3. When waking up from S3 state the IMR boot will fail as the content is lost. Set the skip_imr_boot flag to make sure that we don't try IMR in this case. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Liam Girdwood Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240322112504.4192-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 31ffa1a8f2ac..ef5c915db8ff 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -681,17 +681,27 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); + bool imr_lost = false; int ret, j; /* - * The memory used for IMR boot loses its content in deeper than S3 state - * We must not try IMR boot on next power up (as it will fail). - * + * The memory used for IMR boot loses its content in deeper than S3 + * state on CAVS platforms. + * On ACE platforms due to the system architecture the IMR content is + * lost at S3 state already, they are tailored for s2idle use. + * We must not try IMR boot on next power up in these cases as it will + * fail. + */ + if (sdev->system_suspend_target > SOF_SUSPEND_S3 || + (chip->hw_ip_version >= SOF_INTEL_ACE_1_0 && + sdev->system_suspend_target == SOF_SUSPEND_S3)) + imr_lost = true; + + /* * In case of firmware crash or boot failure set the skip_imr_boot to true * as well in order to try to re-load the firmware to do a 'cold' boot. */ - if (sdev->system_suspend_target > SOF_SUSPEND_S3 || - sdev->fw_state == SOF_FW_CRASHED || + if (imr_lost || sdev->fw_state == SOF_FW_CRASHED || sdev->fw_state == SOF_FW_BOOT_FAILED) hda->skip_imr_boot = true; -- cgit v1.2.3 From e2d7ad717a6b0880843dbc60855a5b97ad0395f8 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 25 Mar 2024 14:44:50 +0000 Subject: ASoC: cs-amp-lib: Check for no firmware controls when writing calibration When a wmfw file has not been loaded the firmware control descriptions necessary to write a stored calibration are not present. In this case print a more descriptive error message. The message is logged at info level because it is not fatal, and does not necessarily imply that anything is broken. Signed-off-by: Simon Trimmer Signed-off-by: Richard Fitzgerald Link: https://msgid.link/r/20240325144450.293630-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 01ef4db5407d..287ac01a3873 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -56,6 +56,11 @@ static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", data->calAmbient, data->calStatus, data->calR); + if (list_empty(&dsp->ctl_list)) { + dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n"); + return -ENOENT; + } + ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); if (ret) return ret; -- cgit v1.2.3 From 708181c50b7763c689ecaba5db8075c2d03719c4 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 22 Mar 2024 13:27:03 +0200 Subject: ASoC: SOF: mtrace: rework mtrace timestamp setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original timestamp is built base on windows epoch time which is not fit for Linux system and difficult to be used for kernel debugging. This patch adopts syslog timestamp so that we can simply use dmesg to check the timestamp between fw and kernel. Signed-off-by: Rander Wang Reviewed-by: Péter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Signed-off-by: Peter Ujfalusi Link: https://msgid.link/r/20240322112703.4549-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-mtrace.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c index 9f1e33ee8826..0e04bea9432d 100644 --- a/sound/soc/sof/ipc4-mtrace.c +++ b/sound/soc/sof/ipc4-mtrace.c @@ -4,6 +4,7 @@ #include #include +#include #include #include "sof-priv.h" #include "ipc4-priv.h" @@ -412,7 +413,6 @@ static int ipc4_mtrace_enable(struct snd_sof_dev *sdev) const struct sof_ipc_ops *iops = sdev->ipc->ops; struct sof_ipc4_msg msg; u64 system_time; - ktime_t kt; int ret; if (priv->mtrace_state != SOF_MTRACE_DISABLED) @@ -424,9 +424,12 @@ static int ipc4_mtrace_enable(struct snd_sof_dev *sdev) msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID); msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_SYSTEM_TIME); - /* The system time is in usec, UTC, epoch is 1601-01-01 00:00:00 */ - kt = ktime_add_us(ktime_get_real(), FW_EPOCH_DELTA * USEC_PER_SEC); - system_time = ktime_to_us(kt); + /* + * local_clock() is used to align with dmesg, so both kernel and firmware logs have + * the same base and a minor delta due to the IPC. system time is in us format but + * local_clock() returns the time in ns, so convert to ns. + */ + system_time = div64_u64(local_clock(), NSEC_PER_USEC); msg.data_size = sizeof(system_time); msg.data_ptr = &system_time; ret = iops->set_get_data(sdev, &msg, msg.data_size, true); -- cgit v1.2.3 From 56ebbd19c2989f7450341f581e2724a149d0f08e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Mar 2024 10:54:34 +0000 Subject: ASoC: cs42l43: Correct extraction of data pointer in suspend/resume The current code is pulling the wrong pointer causing it to disable the wrong IRQ. Correct the code to pull the correct cs42l43 core data pointer. Fixes: 64353af49fec ("ASoC: cs42l43: Add system suspend ops to disable IRQ") Signed-off-by: Charles Keepax Link: https://msgid.link/r/20240326105434.852907-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l43.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 860d5cda67bf..94685449f0f4 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2364,7 +2364,8 @@ static int cs42l43_codec_runtime_resume(struct device *dev) static int cs42l43_codec_suspend(struct device *dev) { - struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; disable_irq(cs42l43->irq); @@ -2373,7 +2374,8 @@ static int cs42l43_codec_suspend(struct device *dev) static int cs42l43_codec_suspend_noirq(struct device *dev) { - struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; enable_irq(cs42l43->irq); @@ -2382,7 +2384,8 @@ static int cs42l43_codec_suspend_noirq(struct device *dev) static int cs42l43_codec_resume(struct device *dev) { - struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; enable_irq(cs42l43->irq); @@ -2391,7 +2394,8 @@ static int cs42l43_codec_resume(struct device *dev) static int cs42l43_codec_resume_noirq(struct device *dev) { - struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + struct cs42l43_codec *priv = dev_get_drvdata(dev); + struct cs42l43 *cs42l43 = priv->core; disable_irq(cs42l43->irq); -- cgit v1.2.3 From 4af565de9f8c74b9f6035924ce0d40adec211246 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 27 Mar 2024 16:16:53 +0530 Subject: ASoC: amd: acp: fix for acp pdm configuration check ACP PDM configuration has to be verified for all combinations. Remove FLAG_AMD_LEGACY_ONLY_DMIC check. Fixes: 3a94c8ad0aae ("ASoC: amd: acp: add code for scanning acp pdm controller") Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240327104657.3537664-2-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-pci.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 8c8b1dcac628..440b91d4f261 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -133,11 +133,9 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } } - if (flag == FLAG_AMD_LEGACY_ONLY_DMIC) { - ret = check_acp_pdm(pci, chip); - if (ret < 0) - goto skip_pdev_creation; - } + ret = check_acp_pdm(pci, chip); + if (ret < 0) + goto skip_pdev_creation; chip->flag = flag; memset(&pdevinfo, 0, sizeof(pdevinfo)); -- cgit v1.2.3 From 310a5caa4e861616a27a83c3e8bda17d65026fa8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:12 -0500 Subject: ASoC: rt5682-sdw: fix locking sequence The disable_irq_lock protects the 'disable_irq' value, we need to lock before testing it. Fixes: 02fb23d72720 ("ASoC: rt5682-sdw: fix for JD event handling in ClockStop Mode0") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Chao Song Link: https://msgid.link/r/20240325221817.206465-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index e67c2e19cb1a..1fdbef5fd6cb 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -763,12 +763,12 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt5682->disable_irq_lock); if (rt5682->disable_irq == true) { - mutex_lock(&rt5682->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); rt5682->disable_irq = false; - mutex_unlock(&rt5682->disable_irq_lock); } + mutex_unlock(&rt5682->disable_irq_lock); goto regmap_sync; } -- cgit v1.2.3 From ee287771644394d071e6a331951ee8079b64f9a7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:13 -0500 Subject: ASoC: rt711-sdca: fix locking sequence The disable_irq_lock protects the 'disable_irq' value, we need to lock before testing it. Fixes: 23adeb7056ac ("ASoC: rt711-sdca: fix for JD event handling in ClockStop Mode0") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Chao Song Link: https://msgid.link/r/20240325221817.206465-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdca-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 935e597022d3..b8471b2d8f4f 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -438,13 +438,13 @@ static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt711->disable_irq_lock); if (rt711->disable_irq == true) { - mutex_lock(&rt711->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt711->disable_irq = false; - mutex_unlock(&rt711->disable_irq_lock); } + mutex_unlock(&rt711->disable_irq_lock); goto regmap_sync; } -- cgit v1.2.3 From aae86cfd8790bcc7693a5a0894df58de5cb5128c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:14 -0500 Subject: ASoC: rt711-sdw: fix locking sequence The disable_irq_lock protects the 'disable_irq' value, we need to lock before testing it. Fixes: b69de265bd0e ("ASoC: rt711: fix for JD event handling in ClockStop Mode0") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Chao Song Link: https://msgid.link/r/20240325221817.206465-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 3f5773310ae8..988451f24a75 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -536,12 +536,12 @@ static int __maybe_unused rt711_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt711->disable_irq_lock); if (rt711->disable_irq == true) { - mutex_lock(&rt711->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); rt711->disable_irq = false; - mutex_unlock(&rt711->disable_irq_lock); } + mutex_unlock(&rt711->disable_irq_lock); goto regmap_sync; } -- cgit v1.2.3 From c8b2e5c1b959d100990e4f0cbad38e7d047bb97c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:15 -0500 Subject: ASoC: rt712-sdca-sdw: fix locking sequence The disable_irq_lock protects the 'disable_irq' value, we need to lock before testing it. Fixes: 7a8735c1551e ("ASoC: rt712-sdca: fix for JD event handling in ClockStop Mode0") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Chao Song Link: https://msgid.link/r/20240325221817.206465-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt712-sdca-sdw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 01ac555cd79b..36d0dd532b8d 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -438,13 +438,14 @@ static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt712->disable_irq_lock); if (rt712->disable_irq == true) { - mutex_lock(&rt712->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt712->disable_irq = false; - mutex_unlock(&rt712->disable_irq_lock); } + mutex_unlock(&rt712->disable_irq_lock); goto regmap_sync; } -- cgit v1.2.3 From adb354bbc231b23d3a05163ce35c1d598512ff64 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:16 -0500 Subject: ASoC: rt722-sdca-sdw: fix locking sequence The disable_irq_lock protects the 'disable_irq' value, we need to lock before testing it. Fixes: a0b7c59ac1a9 ("ASoC: rt722-sdca: fix for JD event handling in ClockStop Mode0") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Chao Song Link: https://msgid.link/r/20240325221817.206465-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca-sdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index eb76f4c675b6..65d584c1886e 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -467,13 +467,13 @@ static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) return 0; if (!slave->unattach_request) { + mutex_lock(&rt722->disable_irq_lock); if (rt722->disable_irq == true) { - mutex_lock(&rt722->disable_irq_lock); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_6); sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); rt722->disable_irq = false; - mutex_unlock(&rt722->disable_irq_lock); } + mutex_unlock(&rt722->disable_irq_lock); goto regmap_sync; } -- cgit v1.2.3 From f892e66fcabc6161cd38c0fc86e769208174b840 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 25 Mar 2024 17:18:17 -0500 Subject: ASoC: rt-sdw*: add __func__ to all error logs The drivers for Realtek SoundWire codecs use similar logs, which is problematic to analyze problems reported by CI tools, e.g. "Failed to get private value: 752001 => 0000 ret=-5". It's not uncommon to have several Realtek devices on the same platform, having the same log thrown makes support difficult. This patch adds __func__ to all error logs which didn't already include it. No functionality change, only error logs are modified. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://msgid.link/r/20240325221817.206465-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1316-sdw.c | 8 +++---- sound/soc/codecs/rt1318-sdw.c | 8 +++---- sound/soc/codecs/rt5682-sdw.c | 12 +++++----- sound/soc/codecs/rt700.c | 16 ++++++------- sound/soc/codecs/rt711-sdca-sdw.c | 2 +- sound/soc/codecs/rt711-sdca.c | 18 +++++++-------- sound/soc/codecs/rt711-sdw.c | 4 ++-- sound/soc/codecs/rt711.c | 16 ++++++------- sound/soc/codecs/rt712-sdca-dmic.c | 24 +++++++++++--------- sound/soc/codecs/rt712-sdca-sdw.c | 2 +- sound/soc/codecs/rt712-sdca.c | 20 ++++++++--------- sound/soc/codecs/rt715-sdca-sdw.c | 2 +- sound/soc/codecs/rt715-sdca.c | 46 +++++++++++++++++++------------------- sound/soc/codecs/rt715-sdw.c | 4 ++-- sound/soc/codecs/rt715.c | 24 ++++++++++---------- sound/soc/codecs/rt722-sdca.c | 21 ++++++++--------- 16 files changed, 115 insertions(+), 112 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 47511f70119a..0b3bf920bcab 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -537,7 +537,7 @@ static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -577,12 +577,12 @@ static int rt1316_sdw_parse_dt(struct rt1316_sdw_priv *rt1316, struct device *de if (rt1316->bq_params_cnt) { rt1316->bq_params = devm_kzalloc(dev, rt1316->bq_params_cnt, GFP_KERNEL); if (!rt1316->bq_params) { - dev_err(dev, "Could not allocate bq_params memory\n"); + dev_err(dev, "%s: Could not allocate bq_params memory\n", __func__); ret = -ENOMEM; } else { ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1316->bq_params, rt1316->bq_params_cnt); if (ret < 0) - dev_err(dev, "Could not read list of realtek,bq-params\n"); + dev_err(dev, "%s: Could not read list of realtek,bq-params\n", __func__); } } @@ -759,7 +759,7 @@ static int __maybe_unused rt1316_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT1316_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index ff364bde4a08..462c9a4b1be5 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -606,7 +606,7 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -631,8 +631,8 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, sampling_rate = RT1318_SDCA_RATE_192000HZ; break; default: - dev_err(component->dev, "Rate %d is not supported\n", - params_rate(params)); + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); return -EINVAL; } @@ -835,7 +835,7 @@ static int __maybe_unused rt1318_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT1318_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); return -ETIMEDOUT; } diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 1fdbef5fd6cb..f9ee42c13dba 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -132,7 +132,7 @@ static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt5682->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -315,8 +315,8 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, &rt5682_sdw_indirect_regmap); if (IS_ERR(rt5682->regmap)) { ret = PTR_ERR(rt5682->regmap); - dev_err(dev, "Failed to allocate register map: %d\n", - ret); + dev_err(dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); return ret; } @@ -400,7 +400,7 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) } if (val != DEVICE_ID) { - dev_err(dev, "Device with ID register %x is not rt5682\n", val); + dev_err(dev, "%s: Device with ID register %x is not rt5682\n", __func__, val); ret = -ENODEV; goto err_nodev; } @@ -648,7 +648,7 @@ static int rt5682_bus_config(struct sdw_slave *slave, ret = rt5682_clock_config(&slave->dev); if (ret < 0) - dev_err(&slave->dev, "Invalid clk config"); + dev_err(&slave->dev, "%s: Invalid clk config", __func__); return ret; } @@ -775,7 +775,7 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 0ebf344a1b60..434b926f96c8 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -37,8 +37,8 @@ static int rt700_index_write(struct regmap *regmap, ret = regmap_write(regmap, addr, value); if (ret < 0) - pr_err("Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -52,8 +52,8 @@ static int rt700_index_read(struct regmap *regmap, *value = 0; ret = regmap_read(regmap, addr, value); if (ret < 0) - pr_err("Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -930,14 +930,14 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, port_config.num += 2; break; default: - dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id); return -EINVAL; } retval = sdw_stream_add_slave(rt700->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -945,8 +945,8 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, /* bit 3:0 Number of Channel */ val |= (params_channels(params) - 1); } else { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index b8471b2d8f4f..2636c2eea4bc 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -451,7 +451,7 @@ static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT711_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 447154cb6010..1e8dbfc3ecd9 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -36,8 +36,8 @@ static int rt711_sdca_index_write(struct rt711_sdca_priv *rt711, ret = regmap_write(regmap, addr, value); if (ret < 0) dev_err(&rt711->slave->dev, - "Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + "%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -52,8 +52,8 @@ static int rt711_sdca_index_read(struct rt711_sdca_priv *rt711, ret = regmap_read(regmap, addr, value); if (ret < 0) dev_err(&rt711->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + "%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -1293,13 +1293,13 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt711->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } if (params_channels(params) > 16) { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } @@ -1318,8 +1318,8 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream, sampling_rate = RT711_SDCA_RATE_192000HZ; break; default: - dev_err(component->dev, "Rate %d is not supported\n", - params_rate(params)); + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 988451f24a75..0d3b43dd22e6 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -408,7 +408,7 @@ static int rt711_bus_config(struct sdw_slave *slave, ret = rt711_clock_config(&slave->dev); if (ret < 0) - dev_err(&slave->dev, "Invalid clk config"); + dev_err(&slave->dev, "%s: Invalid clk config", __func__); return ret; } @@ -548,7 +548,7 @@ static int __maybe_unused rt711_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT711_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); return -ETIMEDOUT; } diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 66eaed13b0d6..5446f9506a16 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -37,8 +37,8 @@ static int rt711_index_write(struct regmap *regmap, ret = regmap_write(regmap, addr, value); if (ret < 0) - pr_err("Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -52,8 +52,8 @@ static int rt711_index_read(struct regmap *regmap, *value = 0; ret = regmap_read(regmap, addr, value); if (ret < 0) - pr_err("Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -428,7 +428,7 @@ static void rt711_jack_init(struct rt711_priv *rt711) RT711_HP_JD_FINAL_RESULT_CTL_JD12); break; default: - dev_warn(rt711->component->dev, "Wrong JD source\n"); + dev_warn(rt711->component->dev, "%s: Wrong JD source\n", __func__); break; } @@ -1020,7 +1020,7 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt711->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -1028,8 +1028,8 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, /* bit 3:0 Number of Channel */ val |= (params_channels(params) - 1); } else { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 0926b26619bd..012b79e72cf6 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -139,8 +139,8 @@ static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712, ret = regmap_write(regmap, addr, value); if (ret < 0) dev_err(&rt712->slave->dev, - "Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + "%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -155,8 +155,8 @@ static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712, ret = regmap_read(regmap, addr, value); if (ret < 0) dev_err(&rt712->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + "%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -317,7 +317,8 @@ static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, for (i = 0; i < p->count; i++) { err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]); if (err < 0) - dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + dev_err(&rt712->slave->dev, "%s: 0x%08x can't be set\n", + __func__, p->reg_base + i); } return changed; @@ -667,13 +668,13 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt712->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } if (params_channels(params) > 4) { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } @@ -698,8 +699,8 @@ static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, sampling_rate = RT712_SDCA_RATE_192000HZ; break; default: - dev_err(component->dev, "Rate %d is not supported\n", - params_rate(params)); + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); return -EINVAL; } @@ -923,7 +924,8 @@ static int __maybe_unused rt712_sdca_dmic_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT712_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", + __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 36d0dd532b8d..4e9ab3ef135b 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -452,7 +452,7 @@ static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT712_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 6954fbe7ec5f..b503de9fda80 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -34,8 +34,8 @@ static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712, ret = regmap_write(regmap, addr, value); if (ret < 0) dev_err(&rt712->slave->dev, - "Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + "%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -50,8 +50,8 @@ static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712, ret = regmap_read(regmap, addr, value); if (ret < 0) dev_err(&rt712->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + "%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -1060,13 +1060,13 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt712->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } if (params_channels(params) > 16) { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } @@ -1085,8 +1085,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, sampling_rate = RT712_SDCA_RATE_192000HZ; break; default: - dev_err(component->dev, "Rate %d is not supported\n", - params_rate(params)); + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); return -EINVAL; } @@ -1106,7 +1106,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, sampling_rate); break; default: - dev_err(component->dev, "Wrong DAI id\n"); + dev_err(component->dev, "%s: Wrong DAI id\n", __func__); return -EINVAL; } diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index ab54a67a27eb..ee450126106f 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -237,7 +237,7 @@ static int __maybe_unused rt715_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->enumeration_complete, msecs_to_jiffies(RT715_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + dev_err(&slave->dev, "%s: Enumeration not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 4533eedd7e18..3fb7b9adb61d 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -41,8 +41,8 @@ static int rt715_sdca_index_write(struct rt715_sdca_priv *rt715, ret = regmap_write(regmap, addr, value); if (ret < 0) dev_err(&rt715->slave->dev, - "Failed to set private value: %08x <= %04x %d\n", - addr, value, ret); + "%s: Failed to set private value: %08x <= %04x %d\n", + __func__, addr, value, ret); return ret; } @@ -59,8 +59,8 @@ static int rt715_sdca_index_read(struct rt715_sdca_priv *rt715, ret = regmap_read(regmap, addr, value); if (ret < 0) dev_err(&rt715->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + "%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -152,8 +152,8 @@ static int rt715_sdca_set_amp_gain_put(struct snd_kcontrol *kcontrol, mc->shift); ret = regmap_write(rt715->mbq_regmap, mc->reg + i, gain_val); if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", - mc->reg + i, gain_val); + dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n", + __func__, mc->reg + i, gain_val); return ret; } } @@ -188,8 +188,8 @@ static int rt715_sdca_set_amp_gain_4ch_put(struct snd_kcontrol *kcontrol, ret = regmap_write(rt715->mbq_regmap, reg_base + i, gain_val); if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", - reg_base + i, gain_val); + dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n", + __func__, reg_base + i, gain_val); return ret; } } @@ -224,8 +224,8 @@ static int rt715_sdca_set_amp_gain_8ch_put(struct snd_kcontrol *kcontrol, reg = i < 7 ? reg_base + i : (reg_base - 1) | BIT(15); ret = regmap_write(rt715->mbq_regmap, reg, gain_val); if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", - reg, gain_val); + dev_err(component->dev, "%s: Failed to write 0x%x=0x%x\n", + __func__, reg, gain_val); return ret; } } @@ -246,8 +246,8 @@ static int rt715_sdca_set_amp_gain_get(struct snd_kcontrol *kcontrol, for (i = 0; i < 2; i++) { ret = regmap_read(rt715->mbq_regmap, mc->reg + i, &val); if (ret < 0) { - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", - mc->reg + i, ret); + dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n", + __func__, mc->reg + i, ret); return ret; } ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, mc->shift); @@ -271,8 +271,8 @@ static int rt715_sdca_set_amp_gain_4ch_get(struct snd_kcontrol *kcontrol, for (i = 0; i < 4; i++) { ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val); if (ret < 0) { - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", - reg_base + i, ret); + dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n", + __func__, reg_base + i, ret); return ret; } ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, gain_sft); @@ -297,8 +297,8 @@ static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol, for (i = 0; i < 8; i += 2) { ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val_l); if (ret < 0) { - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", - reg_base + i, ret); + dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n", + __func__, reg_base + i, ret); return ret; } ucontrol->value.integer.value[i] = (val_l >> gain_sft) / 10; @@ -306,8 +306,8 @@ static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol, reg = (i == 6) ? (reg_base - 1) | BIT(15) : reg_base + 1 + i; ret = regmap_read(rt715->mbq_regmap, reg, &val_r); if (ret < 0) { - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", - reg, ret); + dev_err(component->dev, "%s: Failed to read 0x%x, ret=%d\n", + __func__, reg, ret); return ret; } ucontrol->value.integer.value[i + 1] = (val_r >> gain_sft) / 10; @@ -834,15 +834,15 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream, 0xaf00); break; default: - dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id); return -EINVAL; } retval = sdw_stream_add_slave(rt715->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(component->dev, "Unable to configure port, retval:%d\n", - retval); + dev_err(component->dev, "%s: Unable to configure port, retval:%d\n", + __func__, retval); return retval; } @@ -893,8 +893,8 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream, val = 0xf; break; default: - dev_err(component->dev, "Unsupported sample rate %d\n", - params_rate(params)); + dev_err(component->dev, "%s: Unsupported sample rate %d\n", + __func__, params_rate(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 21f37babd148..7e13868ff99f 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -482,7 +482,7 @@ static int rt715_bus_config(struct sdw_slave *slave, ret = rt715_clock_config(&slave->dev); if (ret < 0) - dev_err(&slave->dev, "Invalid clk config"); + dev_err(&slave->dev, "%s: Invalid clk config", __func__); return 0; } @@ -554,7 +554,7 @@ static int __maybe_unused rt715_dev_resume(struct device *dev) time = wait_for_completion_timeout(&slave->initialization_complete, msecs_to_jiffies(RT715_PROBE_TIMEOUT)); if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); sdw_show_ping_status(slave->bus, true); return -ETIMEDOUT; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 9f732a5abd53..299c9b12377c 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -40,8 +40,8 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg, ret = regmap_write(regmap, addr, value); if (ret < 0) { - pr_err("Failed to set private value: %08x <= %04x %d\n", - addr, value, ret); + pr_err("%s: Failed to set private value: %08x <= %04x %d\n", + __func__, addr, value, ret); } return ret; @@ -55,8 +55,8 @@ static int rt715_index_write_nid(struct regmap *regmap, ret = regmap_write(regmap, addr, value); if (ret < 0) - pr_err("Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + pr_err("%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -70,8 +70,8 @@ static int rt715_index_read_nid(struct regmap *regmap, *value = 0; ret = regmap_read(regmap, addr, value); if (ret < 0) - pr_err("Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + pr_err("%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -862,14 +862,14 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000); break; default: - dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + dev_err(component->dev, "%s: Invalid DAI id %d\n", __func__, dai->id); return -EINVAL; } retval = sdw_stream_add_slave(rt715->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } @@ -883,8 +883,8 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, val |= 0x0 << 8; break; default: - dev_err(component->dev, "Unsupported sample rate %d\n", - params_rate(params)); + dev_err(component->dev, "%s: Unsupported sample rate %d\n", + __func__, params_rate(params)); return -EINVAL; } @@ -892,8 +892,8 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, /* bit 3:0 Number of Channel */ val |= (params_channels(params) - 1); } else { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 0e1c65a20392..e0ea3a23f7cc 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -35,8 +35,8 @@ int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, ret = regmap_write(regmap, addr, value); if (ret < 0) dev_err(&rt722->slave->dev, - "Failed to set private value: %06x <= %04x ret=%d\n", - addr, value, ret); + "%s: Failed to set private value: %06x <= %04x ret=%d\n", + __func__, addr, value, ret); return ret; } @@ -51,8 +51,8 @@ int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, ret = regmap_read(regmap, addr, value); if (ret < 0) dev_err(&rt722->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); + "%s: Failed to get private value: %06x => %04x ret=%d\n", + __func__, addr, *value, ret); return ret; } @@ -663,7 +663,8 @@ static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, for (i = 0; i < p->count; i++) { err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); if (err < 0) - dev_err(&rt722->slave->dev, "%#08x can't be set\n", p->reg_base + i); + dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n", + __func__, p->reg_base + i); } return changed; @@ -1211,13 +1212,13 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, retval = sdw_stream_add_slave(rt722->slave, &stream_config, &port_config, 1, sdw_stream); if (retval) { - dev_err(dai->dev, "Unable to configure port\n"); + dev_err(dai->dev, "%s: Unable to configure port\n", __func__); return retval; } if (params_channels(params) > 16) { - dev_err(component->dev, "Unsupported channels %d\n", - params_channels(params)); + dev_err(component->dev, "%s: Unsupported channels %d\n", + __func__, params_channels(params)); return -EINVAL; } @@ -1236,8 +1237,8 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, sampling_rate = RT722_SDCA_RATE_192000HZ; break; default: - dev_err(component->dev, "Rate %d is not supported\n", - params_rate(params)); + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); return -EINVAL; } -- cgit v1.2.3 From fc563aa900659a850e2ada4af26b9d7a3de6c591 Mon Sep 17 00:00:00 2001 From: Stephen Lee Date: Mon, 25 Mar 2024 18:01:31 -0700 Subject: ASoC: ops: Fix wraparound for mask in snd_soc_get_volsw In snd_soc_info_volsw(), mask is generated by figuring out the index of the most significant bit set in max and converting the index to a bitmask through bit shift 1. Unintended wraparound occurs when max is an integer value with msb bit set. Since the bit shift value 1 is treated as an integer type, the left shift operation will wraparound and set mask to 0 instead of all 1's. In order to fix this, we type cast 1 as `1ULL` to prevent the wraparound. Fixes: 7077148fb50a ("ASoC: core: Split ops out of soc-core.c") Signed-off-by: Stephen Lee Link: https://msgid.link/r/20240326010131.6211-1-slee08177@gmail.com Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 2d25748ca706..b27e89ff6a16 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -263,7 +263,7 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; int min = mc->min; int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; + unsigned int mask = (1ULL << fls(max)) - 1; unsigned int invert = mc->invert; int val; int ret; -- cgit v1.2.3 From 2c603a4947a1247102ccb008d5eb6f37a4043c98 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Fri, 29 Mar 2024 11:08:12 +0530 Subject: ASoC: amd: acp: fix for acp_init function error handling If acp_init() fails, acp pci driver probe should return error. Add acp_init() function return value check logic. Fixes: e61b415515d3 ("ASoC: amd: acp: refactor the acp init and de-init sequence") Signed-off-by: Vijendar Mukunda Link: https://lore.kernel.org/r/20240329053815.2373979-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp/acp-pci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 440b91d4f261..5f35b90eab8d 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -115,7 +115,10 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id goto unregister_dmic_dev; } - acp_init(chip); + ret = acp_init(chip); + if (ret) + goto unregister_dmic_dev; + res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL); if (!res) { ret = -ENOMEM; -- cgit v1.2.3 From 8a655cee6c9d4588570ad0cb099c5660f9a44a12 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 2 Apr 2024 14:20:40 +0800 Subject: ASoC: codecs: ES8326: Solve error interruption issue We got an error report about headphone type detection and button detection. We fixed the headphone type detection error by adjusting the debounce timer configuration. And we fixed the button detection error by disabling the button detection feature when the headphone are unplugged and enabling it when headphone are plugged in. Signed-off-by: Zhang Yi Link: https://msgid.link/r/20240402062043.20608-2-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 15289dadafea..a6783fd6553d 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -843,6 +843,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a); regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); /* * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event */ @@ -865,6 +866,8 @@ static void es8326_jack_detect_handler(struct work_struct *work) * set auto-check mode, then restart jack_detect_work after 400ms. * Don't report jack status. */ + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); @@ -987,7 +990,7 @@ static int es8326_resume(struct snd_soc_component *component) regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); usleep_range(10000, 15000); - regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xe9); + regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xd9); regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xcb); /* set headphone default type and detect pin */ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83); @@ -1038,8 +1041,7 @@ static int es8326_resume(struct snd_soc_component *component) es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, - (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9); regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, -- cgit v1.2.3 From 4581468d071b64a2e3c2ae333fff82dc0391a306 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 2 Apr 2024 14:20:41 +0800 Subject: ASoC: codecs: ES8326: modify clock table We got a digital microphone feature issue. And we fixed it by modifying the clock table. Also, we changed the marco ES8326_CLK_ON declaration Signed-off-by: Zhang Yi Link: https://msgid.link/r/20240402062043.20608-3-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 22 +++++++++++----------- sound/soc/codecs/es8326.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a6783fd6553d..275db81d10d4 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -412,9 +412,9 @@ static const struct _coeff_div coeff_div_v3[] = { {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27}, {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, - {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, - {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, - {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x3F}, + {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, + {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47}, {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47}, {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, @@ -423,10 +423,10 @@ static const struct _coeff_div coeff_div_v3[] = { {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27}, - {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, - {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, - {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, - {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x7F}, + {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x08, 0x19, 0x1F, 0x3F}, + {256, 44100, 11289600, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, + {256, 48000, 12288000, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47}, {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F}, {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, @@ -435,10 +435,10 @@ static const struct _coeff_div coeff_div_v3[] = { {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F}, {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F}, - {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, - {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, - {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, - {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, + {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x08, 0x19, 0x1B, 0x1F, 0x7F}, + {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x08, 0x19, 0x1F, 0x3F}, + {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, + {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F}, {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F}, {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F}, {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F}, diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index ee12caef8105..c3e52e7bdef5 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -104,7 +104,7 @@ #define ES8326_MUTE (3 << 0) /* ES8326_CLK_CTL */ -#define ES8326_CLK_ON (0x7e << 0) +#define ES8326_CLK_ON (0x7f << 0) #define ES8326_CLK_OFF (0 << 0) /* ES8326_CLK_INV */ -- cgit v1.2.3 From 6e5f5bf894eb9260f07ad0da4e2dd2efd616ed59 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 2 Apr 2024 14:20:42 +0800 Subject: ASoC: codecs: ES8326: Solve a headphone detection issue after suspend and resume We got a headphone detection issue after suspend and resume. And we fixed it by modifying the configuration at es8326_suspend and invoke es8326_irq at es8326_resume. Signed-off-by: Zhang Yi Link: https://msgid.link/r/20240402062043.20608-4-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 275db81d10d4..fa809ab41a4a 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -1062,6 +1062,8 @@ static int es8326_resume(struct snd_soc_component *component) es8326->hp = 0; es8326->hpl_vol = 0x03; es8326->hpr_vol = 0x03; + + es8326_irq(es8326->irq, es8326); return 0; } @@ -1072,6 +1074,9 @@ static int es8326_suspend(struct snd_soc_component *component) cancel_delayed_work_sync(&es8326->jack_detect_work); es8326_disable_micbias(component); es8326->calibrated = false; + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d); + regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x00); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF); regcache_cache_only(es8326->regmap, true); regcache_mark_dirty(es8326->regmap); -- cgit v1.2.3 From fec9c7f668ac5dd107f4da5a3b18379e07ec1a41 Mon Sep 17 00:00:00 2001 From: Zhang Yi Date: Tue, 2 Apr 2024 14:20:43 +0800 Subject: ASoC: codecs: ES8326: Removing the control of ADC_SCALE We removed the configuration of ES8326_ADC_SCALE in es8326_jack_detect_handler because user changed the configuration by snd_controls Signed-off-by: Zhang Yi Link: https://msgid.link/r/20240402062043.20608-5-zhangyi@everest-semi.com Signed-off-by: Mark Brown --- sound/soc/codecs/es8326.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index fa809ab41a4a..17bd6b516077 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -835,7 +835,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) dev_dbg(comp->dev, "Report hp remove event\n"); snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); /* mute adc when mic path switch */ - regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44); regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66); es8326->hp = 0; @@ -894,7 +893,6 @@ static void es8326_jack_detect_handler(struct work_struct *work) snd_soc_jack_report(es8326->jack, SND_JACK_HEADSET, SND_JACK_HEADSET); - regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, 0x08, 0x08); regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, -- cgit v1.2.3 From d619b0b70dc4f160f2b95d95ccfed2631ab7ac3a Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Tue, 2 Apr 2024 15:06:40 +0200 Subject: ASoC: Intel: avs: boards: Add modules description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modpost warns about missing module description, add it. Reviewed-by: Cezary Rojewski Signed-off-by: Amadeusz Sławiński Link: https://msgid.link/r/20240402130640.3310999-1-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/boards/da7219.c | 1 + sound/soc/intel/avs/boards/dmic.c | 1 + sound/soc/intel/avs/boards/es8336.c | 1 + sound/soc/intel/avs/boards/i2s_test.c | 1 + sound/soc/intel/avs/boards/max98357a.c | 1 + sound/soc/intel/avs/boards/max98373.c | 1 + sound/soc/intel/avs/boards/max98927.c | 1 + sound/soc/intel/avs/boards/nau8825.c | 1 + sound/soc/intel/avs/boards/probe.c | 1 + sound/soc/intel/avs/boards/rt274.c | 1 + sound/soc/intel/avs/boards/rt286.c | 1 + sound/soc/intel/avs/boards/rt298.c | 1 + sound/soc/intel/avs/boards/rt5514.c | 1 + sound/soc/intel/avs/boards/rt5663.c | 1 + sound/soc/intel/avs/boards/rt5682.c | 1 + sound/soc/intel/avs/boards/ssm4567.c | 1 + 16 files changed, 16 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index c018f84fe025..fc072dc58968 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -296,5 +296,6 @@ static struct platform_driver avs_da7219_driver = { module_platform_driver(avs_da7219_driver); +MODULE_DESCRIPTION("Intel da7219 machine driver"); MODULE_AUTHOR("Cezary Rojewski "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c index ba2bc7f689eb..d9e5e85f5233 100644 --- a/sound/soc/intel/avs/boards/dmic.c +++ b/sound/soc/intel/avs/boards/dmic.c @@ -96,4 +96,5 @@ static struct platform_driver avs_dmic_driver = { module_platform_driver(avs_dmic_driver); +MODULE_DESCRIPTION("Intel DMIC machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 1090082e7d5b..5c90a6007577 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -326,4 +326,5 @@ static struct platform_driver avs_es8336_driver = { module_platform_driver(avs_es8336_driver); +MODULE_DESCRIPTION("Intel es8336 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c index 28f254eb0d03..027373d6a16d 100644 --- a/sound/soc/intel/avs/boards/i2s_test.c +++ b/sound/soc/intel/avs/boards/i2s_test.c @@ -204,4 +204,5 @@ static struct platform_driver avs_i2s_test_driver = { module_platform_driver(avs_i2s_test_driver); +MODULE_DESCRIPTION("Intel i2s test machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index a83b95f25129..1ff85e4d8e16 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -154,4 +154,5 @@ static struct platform_driver avs_max98357a_driver = { module_platform_driver(avs_max98357a_driver) +MODULE_DESCRIPTION("Intel max98357a machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 3b980a025e6f..8d31586b73ea 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -211,4 +211,5 @@ static struct platform_driver avs_max98373_driver = { module_platform_driver(avs_max98373_driver) +MODULE_DESCRIPTION("Intel max98373 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index 86dd2b228df3..572ec58073d0 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -208,4 +208,5 @@ static struct platform_driver avs_max98927_driver = { module_platform_driver(avs_max98927_driver) +MODULE_DESCRIPTION("Intel max98927 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index 1c1e2083f474..55db75efae41 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -313,4 +313,5 @@ static struct platform_driver avs_nau8825_driver = { module_platform_driver(avs_nau8825_driver) +MODULE_DESCRIPTION("Intel nau8825 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c index a9469b5ecb40..8be6887bbc6e 100644 --- a/sound/soc/intel/avs/boards/probe.c +++ b/sound/soc/intel/avs/boards/probe.c @@ -69,4 +69,5 @@ static struct platform_driver avs_probe_mb_driver = { module_platform_driver(avs_probe_mb_driver); +MODULE_DESCRIPTION("Intel probe machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index bfcb8845fd15..1cf524216087 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -276,4 +276,5 @@ static struct platform_driver avs_rt274_driver = { module_platform_driver(avs_rt274_driver); +MODULE_DESCRIPTION("Intel rt274 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index 28d7d86b1cc9..4740bba10570 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -247,4 +247,5 @@ static struct platform_driver avs_rt286_driver = { module_platform_driver(avs_rt286_driver); +MODULE_DESCRIPTION("Intel rt286 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 80f490b9e118..6e409e29f697 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -266,4 +266,5 @@ static struct platform_driver avs_rt298_driver = { module_platform_driver(avs_rt298_driver); +MODULE_DESCRIPTION("Intel rt298 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index 60105f453ae2..097ae5f73241 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -192,4 +192,5 @@ static struct platform_driver avs_rt5514_driver = { module_platform_driver(avs_rt5514_driver); +MODULE_DESCRIPTION("Intel rt5514 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index b4762c2a7bf2..1880c315cc4d 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -265,4 +265,5 @@ static struct platform_driver avs_rt5663_driver = { module_platform_driver(avs_rt5663_driver); +MODULE_DESCRIPTION("Intel rt5663 machine driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 243f979fda98..594a971ded9e 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -341,5 +341,6 @@ static struct platform_driver avs_rt5682_driver = { module_platform_driver(avs_rt5682_driver) +MODULE_DESCRIPTION("Intel rt5682 machine driver"); MODULE_AUTHOR("Cezary Rojewski "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index 4a0e136835ff..d6f7f046c24e 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -200,4 +200,5 @@ static struct platform_driver avs_ssm4567_driver = { module_platform_driver(avs_ssm4567_driver) +MODULE_DESCRIPTION("Intel ssm4567 machine driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3f5eb32513e75eb321919a703800d4e13e9d3ba8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Apr 2024 14:18:39 +0300 Subject: ASoC: SOF: Intel: lnl: Disable DMIC/SSP offload on remove During probe the DMIC/SSP offload is enabled and it is not reversed on remove. Add a remove wrapper for LNL to disable the offload for DMIC and SSP similarly to what is done during probe. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://msgid.link/r/20240403111839.27259-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/lnl.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index d1c73d407e68..aeb4350cce6b 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -29,15 +29,17 @@ static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = { }; /* this helps allows the DSP to setup DMIC/SSP */ -static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus) +static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable) { int ret; - ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP, true); + ret = hdac_bus_eml_enable_offload(bus, true, + AZX_REG_ML_LEPTR_ID_INTEL_SSP, enable); if (ret < 0) return ret; - ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC, true); + ret = hdac_bus_eml_enable_offload(bus, true, + AZX_REG_ML_LEPTR_ID_INTEL_DMIC, enable); if (ret < 0) return ret; @@ -52,7 +54,19 @@ static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev) if (ret < 0) return ret; - return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true); +} + +static void lnl_hda_dsp_remove(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), false); + if (ret < 0) + dev_warn(sdev->dev, + "Failed to disable offload for DMIC/SSP: %d\n", ret); + + hda_dsp_remove(sdev); } static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev) @@ -63,7 +77,7 @@ static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev) if (ret < 0) return ret; - return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true); } static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) @@ -74,7 +88,7 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) if (ret < 0) return ret; - return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true); } static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev) @@ -97,9 +111,11 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev) /* common defaults */ memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); - /* probe */ - if (!sdev->dspless_mode_selected) + /* probe/remove */ + if (!sdev->dspless_mode_selected) { sof_lnl_ops.probe = lnl_hda_dsp_probe; + sof_lnl_ops.remove = lnl_hda_dsp_remove; + } /* shutdown */ sof_lnl_ops.shutdown = hda_dsp_shutdown; -- cgit v1.2.3 From b9846a386734e73a1414950ebfd50f04919f5e24 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 4 Apr 2024 09:47:13 +0530 Subject: ASoC: SOF: amd: fix for false dsp interrupts Before ACP firmware loading, DSP interrupts are not expected. Sometimes after reboot, it's observed that before ACP firmware is loaded false DSP interrupt is reported. Registering the interrupt handler before acp initialization causing false interrupts sometimes on reboot as ACP reset is not applied. Correct the sequence by invoking acp initialization sequence prior to registering interrupt handler. Fixes: 738a2b5e2cc9 ("ASoC: SOF: amd: Add IPC support for ACP IP block") Signed-off-by: Vijendar Mukunda Link: https://msgid.link/r/20240404041717.430545-1-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- sound/soc/sof/amd/acp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index be7dc1e02284..c12c7f820529 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -704,6 +704,10 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) goto unregister_dev; } + ret = acp_init(sdev); + if (ret < 0) + goto free_smn_dev; + sdev->ipc_irq = pci->irq; ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, IRQF_SHARED, "AudioDSP", sdev); @@ -713,10 +717,6 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) goto free_smn_dev; } - ret = acp_init(sdev); - if (ret < 0) - goto free_ipc_irq; - /* scan SoundWire capabilities exposed by DSDT */ ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr); if (ret < 0) { -- cgit v1.2.3 From 90f8917e7a15f6dd508779048bdf00ce119b6ca0 Mon Sep 17 00:00:00 2001 From: Chaitanya Kumar Borah Date: Thu, 4 Apr 2024 13:48:13 -0500 Subject: ASoC: SOF: Core: Add remove_late() to sof_init_environment failure path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where the sof driver is unable to find the firmware and/or topology file [1], it exits without releasing the i915 runtime pm wakeref [2]. This results in dmesg warnings[3] during suspend/resume or driver unbind. Add remove_late() to the failure path of sof_init_environment so that i915 wakeref is released appropriately [1] [ 8.990366] sof-audio-pci-intel-mtl 0000:00:1f.3: SOF firmware and/or topology file not found. [ 8.990396] sof-audio-pci-intel-mtl 0000:00:1f.3: Supported default profiles [ 8.990398] sof-audio-pci-intel-mtl 0000:00:1f.3: - ipc type 1 (Requested): [ 8.990399] sof-audio-pci-intel-mtl 0000:00:1f.3: Firmware file: intel/sof-ipc4/mtl/sof-mtl.ri [ 8.990401] sof-audio-pci-intel-mtl 0000:00:1f.3: Topology file: intel/sof-ace-tplg/sof-mtl-rt711-2ch.tplg [ 8.990402] sof-audio-pci-intel-mtl 0000:00:1f.3: Check if you have 'sof-firmware' package installed. [ 8.990403] sof-audio-pci-intel-mtl 0000:00:1f.3: Optionally it can be manually downloaded from: [ 8.990404] sof-audio-pci-intel-mtl 0000:00:1f.3: https://github.com/thesofproject/sof-bin/ [ 8.999088] sof-audio-pci-intel-mtl 0000:00:1f.3: error: sof_probe_work failed err: -2 [2] ref_tracker: 0000:00:02.0@ffff9b8511b6a378 has 1/5 users at track_intel_runtime_pm_wakeref.part.0+0x36/0x70 [i915] __intel_runtime_pm_get+0x51/0xb0 [i915] intel_runtime_pm_get+0x17/0x20 [i915] intel_display_power_get+0x2f/0x70 [i915] i915_audio_component_get_power+0x23/0x120 [i915] snd_hdac_display_power+0x89/0x130 [snd_hda_core] hda_codec_i915_init+0x3f/0x50 [snd_sof_intel_hda] hda_dsp_probe_early+0x170/0x250 [snd_sof_intel_hda_common] snd_sof_device_probe+0x224/0x320 [snd_sof] sof_pci_probe+0x15b/0x220 [snd_sof_pci] hda_pci_intel_probe+0x30/0x70 [snd_sof_intel_hda_common] local_pci_probe+0x4c/0xb0 pci_device_probe+0xcc/0x250 really_probe+0x18e/0x420 __driver_probe_device+0x7e/0x170 driver_probe_device+0x23/0xa0 [3] [ 484.105070] ------------[ cut here ]------------ [ 484.108238] thunderbolt 0000:00:0d.2: PM: pci_pm_suspend_late+0x0/0x50 returned 0 after 0 usecs [ 484.117106] i915 0000:00:02.0: i915 raw-wakerefs=1 wakelocks=1 on cleanup [ 484.792005] WARNING: CPU: 2 PID: 2405 at drivers/gpu/drm/i915/intel_runtime_pm.c:444 intel_runtime_pm_driver_release+0x6c/0x80 Tested-by: Rodrigo Vivi Reviewed-by: Rodrigo Vivi Reviewed-by: Bard Liao Reviewed-by: Péter Ujfalusi Reviewed-by: Kai Vehmanen Signed-off-by: Chaitanya Kumar Borah Signed-off-by: Pierre-Louis Bossart Acked-by: Lucas De Marchi Link: https://github.com/thesofproject/linux/pull/4878 Signed-off-by: Rodrigo Vivi Link: https://msgid.link/r/20240404184813.134566-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9b00ede2a486..cc84d4c81be9 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -339,8 +339,7 @@ static int sof_init_environment(struct snd_sof_dev *sdev) ret = snd_sof_probe(sdev); if (ret < 0) { dev_err(sdev->dev, "failed to probe DSP %d\n", ret); - sof_ops_free(sdev); - return ret; + goto err_sof_probe; } /* check machine info */ @@ -358,15 +357,18 @@ static int sof_init_environment(struct snd_sof_dev *sdev) ret = validate_sof_ops(sdev); if (ret < 0) { snd_sof_remove(sdev); + snd_sof_remove_late(sdev); return ret; } } + return 0; + err_machine_check: - if (ret) { - snd_sof_remove(sdev); - sof_ops_free(sdev); - } + snd_sof_remove(sdev); +err_sof_probe: + snd_sof_remove_late(sdev); + sof_ops_free(sdev); return ret; } -- cgit v1.2.3 From 7a1625c1711b526a77cb9c3acc15dbba71896a40 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 8 Apr 2024 10:18:40 +0200 Subject: ASoC: Intel: avs: Fix debug window description Recent changes addressed PAGE_SIZE ambiguity in 2/3 locations for struct avs_icl_memwnd2. The unaddressed one causes build errors when PAGE_SIZE != SZ_4K. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202404070100.i3t3Jf7d-lkp@intel.com/ Fixes: 275b583d047a ("ASoC: Intel: avs: ICL-based platforms support") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240408081840.1319431-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/icl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c index 9d9921e1cd4d..d2554c857732 100644 --- a/sound/soc/intel/avs/icl.c +++ b/sound/soc/intel/avs/icl.c @@ -64,7 +64,7 @@ struct avs_icl_memwnd2_desc { struct avs_icl_memwnd2 { union { struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; - u8 rsvd[PAGE_SIZE]; + u8 rsvd[SZ_4K]; }; u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; } __packed; -- cgit v1.2.3 From 2e93a29b48a017c777d4fcbfcc51aba4e6a90d38 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 5 Apr 2024 10:43:06 +0000 Subject: ASoC: tegra: Fix DSPK 16-bit playback DSPK configuration is wrong for 16-bit playback and this happens because the client config is always fixed at 24-bit in hw_params(). Fix this by updating the client config to 16-bit for the respective playback. Fixes: 327ef6470266 ("ASoC: tegra: Add Tegra186 based DSPK driver") Cc: stable@vger.kernel.org Signed-off-by: Sameer Pujar Acked-by: Thierry Reding Link: https://msgid.link/r/20240405104306.551036-1-spujar@nvidia.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra186_dspk.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c index aa37c4ab0adb..21cd41fec7a9 100644 --- a/sound/soc/tegra/tegra186_dspk.c +++ b/sound/soc/tegra/tegra186_dspk.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // tegra186_dspk.c - Tegra186 DSPK driver -// -// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. #include #include @@ -241,14 +240,14 @@ static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - cif_conf.client_bits = TEGRA_ACIF_BITS_24; - switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: cif_conf.audio_bits = TEGRA_ACIF_BITS_16; + cif_conf.client_bits = TEGRA_ACIF_BITS_16; break; case SNDRV_PCM_FORMAT_S32_LE: cif_conf.audio_bits = TEGRA_ACIF_BITS_32; + cif_conf.client_bits = TEGRA_ACIF_BITS_24; break; default: dev_err(dev, "unsupported format!\n"); -- cgit v1.2.3 From e50729d742ec364895f1c389c32315984a987aa5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Apr 2024 21:15:59 +0200 Subject: ASoC: Intel: bytcr_rt5640: Apply Asus T100TA quirk to Asus T100TAM too The Asus T100TA quirk has been using an exact match on a product-name of "T100TA" but there are also T100TAM variants with a slightly higher clocked CPU and a metal backside which need the same quirk. Sort the existing T100TA (stereo speakers) below the more specific T100TAF (mono speaker) quirk and switch from exact matching to substring matching so that the T100TA quirk will also match on the T100TAM models. Signed-off-by: Hans de Goede Link: https://msgid.link/r/20240407191559.21596-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 05f38d1f7d82..b41a1147f1c3 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -636,28 +636,30 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_USE_AMCR0F28), }, { + /* Asus T100TAF, unlike other T100TA* models this one has a mono speaker */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_JD_SRC_JD2_IN4N | BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, { + /* Asus T100TA and T100TAM, must come after T100TAF (mono spk) match */ .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_JD_SRC_JD2_IN4N | BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | - BYT_RT5640_MONO_SPEAKER | - BYT_RT5640_DIFF_MIC | - BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, { -- cgit v1.2.3 From d4884fd48a44f3d7f0d4d7399b663b69c000233d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:02 +0100 Subject: ASoC: cs35l56: Fix unintended bus access while resetting amp Use the new regmap_read_bypassed() so that the regmap can be left in cache-only mode while it is booting, but the driver can still read boot-status and chip-id information during this time. This fixes race conditions where some writes could be issued to the silicon while it is still rebooting, before the driver has determined that the boot is complete. This is typically prevented by putting regmap into cache-only until the hardware is ready. But this assumes that the driver does not need to access device registers to determine when it is "ready". For cs35l56 this involves polling a register and the original implementation relied on having special handlers to block racing callbacks until dsp_work() is complete. However, some cases were missed, most notably the ASP DAI functions. The regmap_read_bypassed() function allows the fix for this to be simplified to putting regmap into cache-only during the reset. The initial boot stages (poll HALO_STATE and read the chip ID) are all done bypassed. Only when the amp is seen to be booted is the cache-only revoked. Changes are: - cs35l56_system_reset() now leaves the regmap in cache-only status. - cs35l56_wait_for_firmware_boot() polls using regmap_read_bypassed(). - cs35l56_init() revokes cache-only either via cs35l56_hw_init() or when firmware has rebooted after a soft reset. - cs35l56_hw_init() exits cache-only after it has determined that the amp has booted. - cs35l56_sdw_init() doesn't disable cache-only, since this must be deferred to cs35l56_init(). - cs35l56_runtime_resume_common() waits for firmware boot before exiting cache-only. These changes cover three situations where the registers are not accessible: 1) SoundWire first-time enumeration. The regmap is kept in cache-only until the chip is fully booted. The original code had to exit cache-only to read chip status in cs35l56_init() and cs35l56_hw_init() but this is now deferred to after the firmware has rebooted. In this case cs35l56_sdw_probe() leaves regmap in cache-only (unchanged behaviour) and cs35l56_hw_init() exits cache-only after the firmware is booted and the chip identified. 2) Soft reset during first-time initialization. cs35l56_init() calls cs35l56_system_reset(), which puts regmap into cache-only. On I2C/SPI cs35l56_init() then flows through to call cs35l56_wait_for_firmware_boot() and exit cache-only. On SoundWire the re-enumeration will enter cs35l56_init() again, which then drops down to call cs35l56_wait_for_firmware_boot() and exit cache-only. 3) Soft reset after firmware download. dsp_work() calls cs35l56_system_reset(), which puts regmap into cache-only. After this the flow is the same as (2). Signed-off-by: Richard Fitzgerald Fixes: 8a731fd37f8b ("ASoC: cs35l56: Move utility functions to shared file") Link: https://msgid.link/r/20240408101803.43183-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-sdw.c | 2 -- sound/soc/codecs/cs35l56-shared.c | 20 ++++++++++++-------- sound/soc/codecs/cs35l56.c | 2 ++ 3 files changed, 14 insertions(+), 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 14a5f86019aa..70ff55c1517f 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -188,8 +188,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) goto out; } - regcache_cache_only(cs35l56->base.regmap, false); - ret = cs35l56_init(cs35l56); if (ret < 0) { regcache_cache_only(cs35l56->base.regmap, true); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 08cac58e3ab2..a83317db75ed 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -307,10 +307,10 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) reg = CS35L56_DSP1_HALO_STATE; /* - * This can't be a regmap_read_poll_timeout() because cs35l56 will NAK - * I2C until it has booted which would terminate the poll + * The regmap must remain in cache-only until the chip has + * booted, so use a bypassed read of the status register. */ - poll_ret = read_poll_timeout(regmap_read, read_ret, + poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), CS35L56_HALO_STATE_POLL_US, CS35L56_HALO_STATE_TIMEOUT_US, @@ -362,7 +362,8 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) return; cs35l56_wait_control_port_ready(); - regcache_cache_only(cs35l56_base->regmap, false); + + /* Leave in cache-only. This will be revoked when the chip has rebooted. */ } EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED); @@ -577,14 +578,14 @@ int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_sou cs35l56_issue_wake_event(cs35l56_base); out_sync: - regcache_cache_only(cs35l56_base->regmap, false); - ret = cs35l56_wait_for_firmware_boot(cs35l56_base); if (ret) { dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); goto err; } + regcache_cache_only(cs35l56_base->regmap, false); + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) goto err; @@ -757,7 +758,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) * devices so the REVID needs to be determined before waiting for the * firmware to boot. */ - ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid); + ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_REVID, &revid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); return ret; @@ -768,7 +769,7 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) if (ret) return ret; - ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid); + ret = regmap_read_bypassed(cs35l56_base->regmap, CS35L56_DEVID, &devid); if (ret < 0) { dev_err(cs35l56_base->dev, "Get Device ID failed\n"); return ret; @@ -787,6 +788,9 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) cs35l56_base->type = devid & 0xFF; + /* Silicon is now identified and booted so exit cache-only */ + regcache_cache_only(cs35l56_base->regmap, false); + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); if (ret) { dev_err(cs35l56_base->dev, "Get Secure status failed\n"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 8d2f021fb362..5a4e0e479414 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1531,6 +1531,8 @@ post_soft_reset: return ret; dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n"); + + regcache_cache_only(cs35l56->base.regmap, false); } /* Disable auto-hibernate so that runtime_pm has control */ -- cgit v1.2.3 From dfd2ffb373999630a14d7ff614440f1c2fcc704c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 8 Apr 2024 11:18:03 +0100 Subject: ASoC: cs35l56: Prevent overwriting firmware ASP config Only populate the ASP1 config registers in the regmap cache if the ASP DAI is used. This prevents regcache_sync() from overwriting these registers with their defaults when the firmware owns control of these registers. On a SoundWire system the ASP could be owned by the firmware to share reference audio with the firmware on other cs35l56. Or it can be used as a normal codec-codec interface owned by the driver. The driver must not overwrite the registers if the firmware has control of them. The original implementation for this in commit 07f7d6e7a124 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") was to still provide defaults for these registers, assuming that if they were never reconfigured from defaults then regcache_sync() would not write them out because they are not dirty. Unfortunately regcache_sync() is not that smart. If the chip has not reset (so the driver has not called regcache_mark_dirty()) a regcache_sync() could write out registers that are not dirty. To avoid accidental overwriting of the ASP registers, they are removed from the table of defaults and instead are populated with defaults only if one of the ASP DAI configuration functions is called. So if the DAI has never been configured, the firmware is assumed to have ownership of these registers, and the regmap cache will not contain any entries for them. Signed-off-by: Richard Fitzgerald Fixes: 07f7d6e7a124 ("ASoC: cs35l56: Fix for initializing ASP1 mixer registers") Link: https://msgid.link/r/20240408101803.43183-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/cs35l56.h | 2 ++ sound/soc/codecs/cs35l56-shared.c | 63 ++++++++++++++++++++++++++------------- sound/soc/codecs/cs35l56.c | 24 ++++++++++++++- 3 files changed, 67 insertions(+), 22 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index e0629699b563..1a3c6f66f620 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -267,6 +267,7 @@ struct cs35l56_base { bool fw_patched; bool secured; bool can_hibernate; + bool fw_owns_asp1; bool cal_data_valid; s8 cal_index; struct cirrus_amp_cal_data cal_data; @@ -283,6 +284,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC]; extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC]; int cs35l56_set_patch(struct cs35l56_base *cs35l56_base); +int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base); int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base); int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command); int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index a83317db75ed..ec1d95e57061 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -40,16 +40,11 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); static const struct reg_default cs35l56_reg_defaults[] = { /* no defaults for OTP_MEM - first read populates cache */ - { CS35L56_ASP1_ENABLES1, 0x00000000 }, - { CS35L56_ASP1_CONTROL1, 0x00000028 }, - { CS35L56_ASP1_CONTROL2, 0x18180200 }, - { CS35L56_ASP1_CONTROL3, 0x00000002 }, - { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, - { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, - { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, - { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, - - /* no defaults for ASP1TX mixer */ + /* + * No defaults for ASP1 control or ASP1TX mixer. See + * cs35l56_populate_asp1_register_defaults() and + * cs35l56_sync_asp1_mixer_widgets_with_firmware(). + */ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, @@ -210,6 +205,36 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } +static const struct reg_sequence cs35l56_asp1_defaults[] = { + REG_SEQ0(CS35L56_ASP1_ENABLES1, 0x00000000), + REG_SEQ0(CS35L56_ASP1_CONTROL1, 0x00000028), + REG_SEQ0(CS35L56_ASP1_CONTROL2, 0x18180200), + REG_SEQ0(CS35L56_ASP1_CONTROL3, 0x00000002), + REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL1, 0x03020100), + REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100), + REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018), + REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018), +}; + +/* + * The firmware can have control of the ASP so we don't provide regmap + * with defaults for these registers, to prevent a regcache_sync() from + * overwriting the firmware settings. But if the machine driver hooks up + * the ASP it means the driver is taking control of the ASP, so then the + * registers are populated with the defaults. + */ +int cs35l56_init_asp1_regs_for_driver_control(struct cs35l56_base *cs35l56_base) +{ + if (!cs35l56_base->fw_owns_asp1) + return 0; + + cs35l56_base->fw_owns_asp1 = false; + + return regmap_multi_reg_write(cs35l56_base->regmap, cs35l56_asp1_defaults, + ARRAY_SIZE(cs35l56_asp1_defaults)); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init_asp1_regs_for_driver_control, SND_SOC_CS35L56_SHARED); + /* * The firmware boot sequence can overwrite the ASP1 config registers so that * they don't match regmap's view of their values. Rewrite the values from the @@ -217,19 +242,15 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) */ int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base) { - struct reg_sequence asp1_regs[] = { - { .reg = CS35L56_ASP1_ENABLES1 }, - { .reg = CS35L56_ASP1_CONTROL1 }, - { .reg = CS35L56_ASP1_CONTROL2 }, - { .reg = CS35L56_ASP1_CONTROL3 }, - { .reg = CS35L56_ASP1_FRAME_CONTROL1 }, - { .reg = CS35L56_ASP1_FRAME_CONTROL5 }, - { .reg = CS35L56_ASP1_DATA_CONTROL1 }, - { .reg = CS35L56_ASP1_DATA_CONTROL5 }, - }; + struct reg_sequence asp1_regs[ARRAY_SIZE(cs35l56_asp1_defaults)]; int i, ret; - /* Read values from regmap cache into a write sequence */ + if (cs35l56_base->fw_owns_asp1) + return 0; + + memcpy(asp1_regs, cs35l56_asp1_defaults, sizeof(asp1_regs)); + + /* Read current values from regmap cache into the write sequence */ for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) { ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def); if (ret) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 5a4e0e479414..6331b8c6136e 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -454,9 +454,14 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); unsigned int val; + int ret; dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; @@ -530,6 +535,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx unsigned int rx_mask, int slots, int slot_width) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + int ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; if ((slots == 0) || (slot_width == 0)) { dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); @@ -578,6 +588,11 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); unsigned int rate = params_rate(params); u8 asp_width, asp_wl; + int ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; asp_wl = params_width(params); if (cs35l56->asp_slot_width) @@ -634,7 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - int freq_id; + int freq_id, ret; + + ret = cs35l56_init_asp1_regs_for_driver_control(&cs35l56->base); + if (ret) + return ret; if (freq == 0) { cs35l56->sysclk_set = false; @@ -1403,6 +1422,9 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56->base.cal_index = -1; cs35l56->speaker_id = -ENOENT; + /* Assume that the firmware owns ASP1 until we know different */ + cs35l56->base.fw_owns_asp1 = true; + dev_set_drvdata(cs35l56->base.dev, cs35l56); cs35l56_fill_supply_names(cs35l56->supplies); -- cgit v1.2.3 From 965e49cdf8c19f21b8308adeded3a8139cff5c84 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:33 +0300 Subject: ASoC: SOF: ipc4-pcm: Use consistent name for snd_sof_pcm_stream pointer Throughout the file the pointer for snd_sof_pcm_stream is named either 'stream' or (wrongly) 'spcm' which confuses the reader. Use 'sps' for the pointer name as it is the most common name used in SOF codebase. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-2-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index e915f9f87a6c..f989cc2992bf 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -764,7 +764,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return 0; } -static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm) +static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps) { struct sof_ipc4_copier *host_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL; @@ -775,7 +775,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc int i; /* find host & dai to locate info in memory window */ - for_each_dapm_widgets(spcm->list, i, widget) { + for_each_dapm_widgets(sps->list, i, widget) { struct snd_sof_widget *swidget = widget->dobj.private; if (!swidget) @@ -795,7 +795,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - info = spcm->private; + info = sps->private; info->host_copier = host_copier; info->dai_copier = dai_copier; info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + @@ -864,7 +864,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_sof_pcm_stream *stream, + struct snd_sof_pcm_stream *sps, struct sof_ipc4_timestamp_info *time_info) { struct sof_ipc4_copier *host_copier = time_info->host_copier; @@ -918,7 +918,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; snd_pcm_uframes_t head_cnt, tail_cnt; - struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm_stream *sps; u64 dai_cnt, host_cnt, host_ptr; struct snd_sof_pcm *spcm; int ret; @@ -927,8 +927,8 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, if (!spcm) return -EOPNOTSUPP; - stream = &spcm->stream[substream->stream]; - time_info = stream->private; + sps = &spcm->stream[substream->stream]; + time_info = sps->private; if (!time_info) return -EOPNOTSUPP; @@ -938,7 +938,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, * the statistics is complete. And it will not change after the first initiailization. */ if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) { - ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info); + ret = sof_ipc4_get_stream_start_offset(sdev, substream, sps, time_info); if (ret < 0) return -EOPNOTSUPP; } @@ -1030,15 +1030,15 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; - struct snd_sof_pcm_stream *stream; + struct snd_sof_pcm_stream *sps; struct snd_sof_pcm *spcm; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return 0; - stream = &spcm->stream[substream->stream]; - time_info = stream->private; + sps = &spcm->stream[substream->stream]; + time_info = sps->private; /* * Report the stored delay value calculated in the pointer callback. * In the unlikely event that the calculation was skipped/aborted, the -- cgit v1.2.3 From 36e980050b0733829e4e0f97b97f7907ba9f00bb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:34 +0300 Subject: ASoC: SOF: ipc4-pcm: Use consistent name for sof_ipc4_timestamp_info pointer The pointer to sof_ipc4_timestamp_info named most of the time as 'time_info' only to be named as 'stream_info' or 'info' in two function. Use the consistent name of 'time_info' throughout the file. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index f989cc2992bf..74b0d0d00270 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -721,7 +721,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; - struct sof_ipc4_timestamp_info *stream_info; + struct sof_ipc4_timestamp_info *time_info; bool support_info = true; u32 abi_version; u32 abi_offset; @@ -752,13 +752,13 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm if (!support_info) continue; - stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL); - if (!stream_info) { + time_info = kzalloc(sizeof(*time_info), GFP_KERNEL); + if (!time_info) { sof_ipc4_pcm_free(sdev, spcm); return -ENOMEM; } - spcm->stream[stream].private = stream_info; + spcm->stream[stream].private = time_info; } return 0; @@ -769,7 +769,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc struct sof_ipc4_copier *host_copier = NULL; struct sof_ipc4_copier *dai_copier = NULL; struct sof_ipc4_llp_reading_slot llp_slot; - struct sof_ipc4_timestamp_info *info; + struct sof_ipc4_timestamp_info *time_info; struct snd_soc_dapm_widget *widget; struct snd_sof_dai *dai; int i; @@ -795,44 +795,44 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - info = sps->private; - info->host_copier = host_copier; - info->dai_copier = dai_copier; - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) + - sdev->fw_info_box.offset; + time_info = sps->private; + time_info->host_copier = host_copier; + time_info->dai_copier = dai_copier; + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_gpdma_reading_slots) + sdev->fw_info_box.offset; /* find llp slot used by current dai */ for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) { - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) break; - info->llp_offset += sizeof(llp_slot); + time_info->llp_offset += sizeof(llp_slot); } if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS) return; /* if no llp gpdma slot is used, check aggregated sdw slot */ - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) + - sdev->fw_info_box.offset; + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_sndw_reading_slots) + sdev->fw_info_box.offset; for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) { - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id) break; - info->llp_offset += sizeof(llp_slot); + time_info->llp_offset += sizeof(llp_slot); } if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS) return; /* check EVAD slot */ - info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) + - sdev->fw_info_box.offset; - sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot)); + time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, + llp_evad_reading_slot) + sdev->fw_info_box.offset; + sof_mailbox_read(sdev, time_info->llp_offset, &llp_slot, sizeof(llp_slot)); if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) - info->llp_offset = 0; + time_info->llp_offset = 0; } static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, -- cgit v1.2.3 From 551af3280c16166244425bbb1d73028f3a907e1f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:35 +0300 Subject: ASoC: SOF: ipc4-pcm: Introduce generic sof_ipc4_pcm_stream_priv Using the sof_ipc4_timestamp_info struct directly as sps->private data is too restrictive, add a new generic sof_ipc4_pcm_stream_priv struct containing the time_info to allow new information to be stored in a generic way. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 74b0d0d00270..34ce6bb7f37d 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -37,6 +37,22 @@ struct sof_ipc4_timestamp_info { snd_pcm_sframes_t delay; }; +/** + * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data + * @time_info: pointer to time info struct if it is supported, otherwise NULL + */ +struct sof_ipc4_pcm_stream_priv { + struct sof_ipc4_timestamp_info *time_info; +}; + +static inline struct sof_ipc4_timestamp_info * +sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps) +{ + struct sof_ipc4_pcm_stream_priv *stream_priv = sps->private; + + return stream_priv->time_info; +} + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { @@ -452,7 +468,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * Invalidate the stream_start_offset to make sure that it is * going to be updated if the stream resumes */ - time_info = spcm->stream[substream->stream].private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); if (time_info) time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION; @@ -706,12 +722,16 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; + struct sof_ipc4_pcm_stream_priv *stream_priv; int stream; for_each_pcm_streams(stream) { pipeline_list = &spcm->stream[stream].pipeline_list; kfree(pipeline_list->pipelines); pipeline_list->pipelines = NULL; + + stream_priv = spcm->stream[stream].private; + kfree(stream_priv->time_info); kfree(spcm->stream[stream].private); spcm->stream[stream].private = NULL; } @@ -721,6 +741,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm { struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_pcm_stream_priv *stream_priv; struct sof_ipc4_timestamp_info *time_info; bool support_info = true; u32 abi_version; @@ -749,6 +770,14 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return -ENOMEM; } + stream_priv = kzalloc(sizeof(*stream_priv), GFP_KERNEL); + if (!stream_priv) { + sof_ipc4_pcm_free(sdev, spcm); + return -ENOMEM; + } + + spcm->stream[stream].private = stream_priv; + if (!support_info) continue; @@ -758,7 +787,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm return -ENOMEM; } - spcm->stream[stream].private = time_info; + stream_priv->time_info = time_info; } return 0; @@ -795,7 +824,7 @@ static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pc return; } - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(sps); time_info->host_copier = host_copier; time_info->dai_copier = dai_copier; time_info->llp_offset = offsetof(struct sof_ipc4_fw_registers, @@ -849,7 +878,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, if (!spcm) return -EINVAL; - time_info = spcm->stream[substream->stream].private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); /* delay calculation is not supported by current fw_reg ABI */ if (!time_info) return 0; @@ -928,7 +957,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, return -EOPNOTSUPP; sps = &spcm->stream[substream->stream]; - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(sps); if (!time_info) return -EOPNOTSUPP; @@ -1030,15 +1059,13 @@ static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; - struct snd_sof_pcm_stream *sps; struct snd_sof_pcm *spcm; spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return 0; - sps = &spcm->stream[substream->stream]; - time_info = sps->private; + time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]); /* * Report the stored delay value calculated in the pointer callback. * In the unlikely event that the calculation was skipped/aborted, the -- cgit v1.2.3 From 7211814f2adcf376b8db6321447a9725c33b6ae7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Apr 2024 14:00:36 +0300 Subject: ASoC: SOF: ipc4-pcm: Do not reset the ChainDMA if it has not been allocated The ChainDMA operation differs from normal pipelines that it is only created when the stream started, in fact a PCM using ChainDMA has no pipelines, modules. To reset a ChainDMA, it needs to be first allocated in firmware. When PulseAudio/PipeWire starts, they will probe the PCMs by opening them, check hw_params and then close the PCM without starting audio. Unconditionally resetting the ChainDMA can result the following error: ipc tx : 0xe040000|0x0: GLB_CHAIN_DMA ipc tx reply: 0x2e000007|0x0: GLB_CHAIN_DMA FW reported error: 7 - Unsupported operation requested ipc error for msg 0xe040000|0x0 sof_pcm_stream_free: pcm_ops hw_free failed -22 Add a new chain_dma_allocated flag to sof_ipc4_pcm_stream_priv to store the ChainDMA allocation state and use this flag to skip sending the reset if the ChainDMA is not allocated. Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Link: https://msgid.link/r/20240409110036.9411-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc4-pcm.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 34ce6bb7f37d..4594470ed08b 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -40,9 +40,12 @@ struct sof_ipc4_timestamp_info { /** * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data * @time_info: pointer to time info struct if it is supported, otherwise NULL + * @chain_dma_allocated: indicates the ChainDMA allocation state */ struct sof_ipc4_pcm_stream_priv { struct sof_ipc4_timestamp_info *time_info; + + bool chain_dma_allocated; }; static inline struct sof_ipc4_timestamp_info * @@ -269,14 +272,17 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, */ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, - int direction, + struct snd_sof_pcm *spcm, int direction, struct snd_sof_pcm_stream_pipeline_list *pipeline_list, int state, int cmd) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_pcm_stream_priv *stream_priv; bool allocate, enable, set_fifo_size; struct sof_ipc4_msg msg = {{ 0 }}; - int i; + int ret, i; + + stream_priv = spcm->stream[direction].private; switch (state) { case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ @@ -297,6 +303,11 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, set_fifo_size = false; break; case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ + + /* ChainDMA can only be reset if it has been allocated */ + if (!stream_priv->chain_dma_allocated) + return 0; + allocate = false; enable = false; set_fifo_size = false; @@ -354,7 +365,12 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, if (enable) msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + /* Update the ChainDMA allocation state */ + if (!ret) + stream_priv->chain_dma_allocated = allocate; + + return ret; } static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, @@ -394,7 +410,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * trigger function that handles the rest for the substream. */ if (pipeline->use_chain_dma) - return sof_ipc4_chain_dma_trigger(sdev, substream->stream, + return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream, pipeline_list, state, cmd); /* allocate memory for the pipeline data */ -- cgit v1.2.3 From 305539a25a1c9929b058381aac6104bd939c0fee Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 8 Apr 2024 14:41:45 -0500 Subject: ASoC: SOF: Intel: add default firmware library path for LNL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit cd6f2a2e6346 ("ASoC: SOF: Intel: Set the default firmware library path for IPC4") added the default_lib_path field for all platforms, but this was missed when LunarLake was later introduced. Fixes: 64a63d9914a5 ("ASoC: SOF: Intel: LNL: Add support for Lunarlake platform") Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://msgid.link/r/20240408194147.28919-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/pci-lnl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index b26ffe767fab..b14e508f1f31 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -35,6 +35,9 @@ static const struct sof_dev_desc lnl_desc = { .default_fw_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4/lnl", }, + .default_lib_path = { + [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/lnl", + }, .default_tplg_path = { [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg", }, -- cgit v1.2.3 From 90a2353080eedec855d63f6aadfda14104ee9b06 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 8 Apr 2024 14:41:46 -0500 Subject: ASoC: SOF: pcm: Restrict DSP D0i3 during S0ix to IPC3 Introduce a new field in struct sof_ipc_pcm_ops that can be used to restrict DSP D0i3 during S0ix suspend to IPC3. With IPC4, all streams must be stopped before S0ix suspend. Reviewed-by: Uday M Bhat Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240408194147.28919-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc3-pcm.c | 1 + sound/soc/sof/pcm.c | 13 ++++++------- sound/soc/sof/sof-audio.h | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 35769dd7905e..af0bf354cb20 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -434,4 +434,5 @@ const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .trigger = sof_ipc3_pcm_trigger, .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, .reset_hw_params_during_stop = true, + .d0i3_supported_in_s0ix = true, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index f03cee94bce6..8804e00e7251 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -325,14 +325,13 @@ static int sof_pcm_trigger(struct snd_soc_component *component, ipc_first = true; break; case SNDRV_PCM_TRIGGER_SUSPEND: - if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && + /* + * If DSP D0I3 is allowed during S0iX, set the suspend_ignored flag for + * D0I3-compatible streams to keep the firmware pipeline running + */ + if (pcm_ops && pcm_ops->d0i3_supported_in_s0ix && + sdev->system_suspend_target == SOF_SUSPEND_S0IX && spcm->stream[substream->stream].d0i3_compatible) { - /* - * trap the event, not sending trigger stop to - * prevent the FW pipelines from being stopped, - * and mark the flag to ignore the upcoming DAPM - * PM events. - */ spcm->stream[substream->stream].suspend_ignored = true; return 0; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 86bbb531e142..499b6084b526 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -116,6 +116,7 @@ struct snd_sof_dai_config_data { * triggers. The FW keeps the host DMA running in this case and * therefore the host must do the same and should stop the DMA during * hw_free. + * @d0i3_supported_in_s0ix: Allow DSP D0I3 during S0iX */ struct sof_ipc_pcm_ops { int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, @@ -135,6 +136,7 @@ struct sof_ipc_pcm_ops { bool reset_hw_params_during_stop; bool ipc_first_on_start; bool platform_stop_during_hw_free; + bool d0i3_supported_in_s0ix; }; /** -- cgit v1.2.3 From 17f4041244e66a417c646c8a90bc6747d5f1de1e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 8 Apr 2024 14:41:47 -0500 Subject: ASoC: SOF: debug: show firmware/topology prefix/names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SOF driver has multiple profiles to select firmware/topology prefix/names depending on the platform and ipc_type, and each of those fields can be overridden with kernel parameters. This results in some cases in confusion on what configuration is actually used in a given test. We currently log the firmware and topology names in the kernel logs, but there's been an ask to add the information in debugfs to simplify test scripts used by developers and CI. This isn't meant to be a stable ABI used by apps, changes will be allowed as needed. Closes: https://github.com/thesofproject/linux/issues/3867 Signed-off-by: Pierre-Louis Bossart Reviewed-by: Péter Ujfalusi Reviewed-by: Bard Liao Link: https://msgid.link/r/20240408194147.28919-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 7c8aafca8fde..7275437ea8d8 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -330,14 +330,32 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); int snd_sof_dbg_init(struct snd_sof_dev *sdev) { + struct snd_sof_pdata *plat_data = sdev->pdata; struct snd_sof_dsp_ops *ops = sof_ops(sdev); const struct snd_sof_debugfs_map *map; + struct dentry *fw_profile; int i; int err; /* use "sof" as top level debugFS dir */ sdev->debugfs_root = debugfs_create_dir("sof", NULL); + /* expose firmware/topology prefix/names for test purposes */ + fw_profile = debugfs_create_dir("fw_profile", sdev->debugfs_root); + + debugfs_create_str("fw_path", 0444, fw_profile, + (char **)&plat_data->fw_filename_prefix); + debugfs_create_str("fw_lib_path", 0444, fw_profile, + (char **)&plat_data->fw_lib_prefix); + debugfs_create_str("tplg_path", 0444, fw_profile, + (char **)&plat_data->tplg_filename_prefix); + debugfs_create_str("fw_name", 0444, fw_profile, + (char **)&plat_data->fw_filename); + debugfs_create_str("tplg_name", 0444, fw_profile, + (char **)&plat_data->tplg_filename); + debugfs_create_u32("ipc_type", 0444, fw_profile, + (u32 *)&plat_data->ipc_type); + /* init dfsentry list */ INIT_LIST_HEAD(&sdev->dfsentry_list); -- cgit v1.2.3 From 4b9a474c7c820391c0913d64431ae9e1f52a5143 Mon Sep 17 00:00:00 2001 From: "end.to.start" Date: Mon, 8 Apr 2024 18:24:54 +0300 Subject: ASoC: acp: Support microphone from device Acer 315-24p This patch adds microphone detection for the Acer 315-24p, after which a microphone appears on the device and starts working Signed-off-by: end.to.start Link: https://msgid.link/r/20240408152454.45532-1-end.to.start@mail.ru Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 69c68d8e7a6b..1760b5d42460 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -430,6 +430,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "MRID6"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "MDC"), + DMI_MATCH(DMI_BOARD_NAME, "Herbag_MDU"), + } + }, { .driver_data = &acp6x_card, .matches = { -- cgit v1.2.3 From 103abab975087e1f01b76fcb54c91dbb65dbc249 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Mon, 8 Apr 2024 17:10:56 +0800 Subject: ASoC: rt5645: Fix the electric noise due to the CBJ contacts floating The codec leaves tie combo jack's sleeve/ring2 to floating status default. It would cause electric noise while connecting the active speaker jack during boot or shutdown. This patch requests a gpio to control the additional jack circuit to tie the contacts to the ground or floating. Signed-off-by: Derek Fang Link: https://msgid.link/r/20240408091057.14165-1-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index e3ba04484813..d0d24a53df74 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -444,6 +444,7 @@ struct rt5645_priv { struct regmap *regmap; struct i2c_client *i2c; struct gpio_desc *gpiod_hp_det; + struct gpio_desc *gpiod_cbj_sleeve; struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; @@ -3186,6 +3187,9 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, 0); + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 1); + msleep(600); regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val); val &= 0x7; @@ -3202,6 +3206,8 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); rt5645->jack_type = SND_JACK_HEADPHONE; + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, @@ -3229,6 +3235,9 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } return rt5645->jack_type; @@ -4012,6 +4021,16 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) return ret; } + rt5645->gpiod_cbj_sleeve = devm_gpiod_get_optional(&i2c->dev, "cbj-sleeve", + GPIOD_OUT_LOW); + + if (IS_ERR(rt5645->gpiod_cbj_sleeve)) { + ret = PTR_ERR(rt5645->gpiod_cbj_sleeve); + dev_info(&i2c->dev, "failed to initialize gpiod, ret=%d\n", ret); + if (ret != -ENOENT) + return ret; + } + for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) rt5645->supplies[i].supply = rt5645_supply_names[i]; @@ -4259,6 +4278,9 @@ static void rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); } @@ -4274,6 +4296,9 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) 0); msleep(20); regmap_write(rt5645->regmap, RT5645_RESET, 0); + + if (rt5645->gpiod_cbj_sleeve) + gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } static int __maybe_unused rt5645_sys_suspend(struct device *dev) -- cgit v1.2.3 From cb9946971d7cb717b726710e1a9fa4ded00b9135 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 9 Apr 2024 06:47:43 +0000 Subject: ASoC: rt722-sdca: modify channel number to support 4 channels Channel numbers of dmic supports 4 channels, modify channels_max regarding to this issue. Signed-off-by: Jack Yu Link: https://msgid.link/r/6a9b1d1fb2ea4f04b2157799f04053b1@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index e0ea3a23f7cc..43bec8dd2ff7 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1330,7 +1330,7 @@ static struct snd_soc_dai_driver rt722_sdca_dai[] = { .capture = { .stream_name = "DP6 DMic Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = RT722_STEREO_RATES, .formats = RT722_FORMATS, }, -- cgit v1.2.3 From 140e0762ca055d1aa84b17847cde5d9e47f56f76 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 9 Apr 2024 06:47:34 +0000 Subject: ASoC: rt722-sdca: add headset microphone vrefo setting Add vrefo settings to fix jd and headset mic recording issue. Signed-off-by: Jack Yu Link: https://msgid.link/r/727219ed45d3485ba8f4646700aaa8a8@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca.c | 25 +++++++++++++++++++------ sound/soc/codecs/rt722-sdca.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 43bec8dd2ff7..e5bd9ef812de 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1439,9 +1439,12 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) int loop_check, chk_cnt = 100, ret; unsigned int calib_status = 0; - /* Read eFuse */ - rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, - 0x4808); + /* Config analog bias */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3, + 0xa081); + /* GE related settings */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2, + 0xa009); /* Button A, B, C, D bypass mode */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, 0xcf00); @@ -1475,9 +1478,6 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) if ((calib_status & 0x0040) == 0x0) break; } - /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ - rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, - 0x0010); /* Set ADC09 power entity floating control */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a12); @@ -1490,8 +1490,21 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) /* Set DAC03 and HP power entity floating control */ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, 0x4040); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1, + 0x4141); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1, + 0x0101); /* Fine tune PDE40 latency */ regmap_write(rt722->regmap, 0x2f58, 0x07); + regmap_write(rt722->regmap, 0x2f03, 0x06); + /* MIC VRefo */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_VREFO_GAT, 0x4000, 0x4000); + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); } int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index 44af8901352e..2464361a7958 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -69,6 +69,7 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_COMBO_JACK_AUTO_CTL2 0x46 #define RT722_COMBO_JACK_AUTO_CTL3 0x47 #define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_VREFO_GAT 0x63 #define RT722_FSM_CTL 0x67 #define RT722_SDCA_INTR_REC 0x82 #define RT722_SW_CONFIG1 0x8a @@ -127,6 +128,8 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_UMP_HID_CTL6 0x66 #define RT722_UMP_HID_CTL7 0x67 #define RT722_UMP_HID_CTL8 0x68 +#define RT722_FLOAT_CTRL_1 0x70 +#define RT722_ENT_FLOAT_CTRL_1 0x76 /* Parameter & Verb control 01 (0x1a)(NID:20h) */ #define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) -- cgit v1.2.3 From eefb831d2e4dd58d58002a2ef75ff989e073230d Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Thu, 11 Apr 2024 15:26:48 +0100 Subject: ASoC: cs35l41: Update DSP1RX5/6 Sources for DSP config Currently, all ASoC systems are set to use VPMON for DSP1RX5_SRC, however, this is required only for internal boost systems. External boost systems require VBSTMON instead of VPMON to be the input to DSP1RX5_SRC. Shared Boost Active acts like Internal boost (requires VPMON). Shared Boost Passive acts like External boost (requires VBSTMON) All systems require DSP1RX6_SRC to be set to VBSTMON. Signed-off-by: Stefan Binding Reviewed-by: Richard Fitzgerald Link: https://msgid.link/r/20240411142648.650921-1-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l41.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index dfb4ce53491b..f8e57a2fc3e3 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1094,6 +1094,7 @@ static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cf static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) { struct wm_adsp *dsp; + uint32_t dsp1rx5_src; int ret; dsp = &cs35l41->dsp; @@ -1113,16 +1114,29 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) return ret; } - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, - CS35L41_INPUT_SRC_VPMON); + switch (cs35l41->hw_cfg.bst_type) { + case CS35L41_INT_BOOST: + case CS35L41_SHD_BOOST_ACTV: + dsp1rx5_src = CS35L41_INPUT_SRC_VPMON; + break; + case CS35L41_EXT_BOOST: + case CS35L41_SHD_BOOST_PASS: + dsp1rx5_src = CS35L41_INPUT_SRC_VBSTMON; + break; + default: + dev_err(cs35l41->dev, "wm_halo_init failed - Invalid Boost Type: %d\n", + cs35l41->hw_cfg.bst_type); + goto err_dsp; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, dsp1rx5_src); if (ret < 0) { - dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + dev_err(cs35l41->dev, "Write DSP1RX5_SRC: %d failed: %d\n", dsp1rx5_src, ret); goto err_dsp; } - ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, - CS35L41_INPUT_SRC_CLASSH); + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_VBSTMON); if (ret < 0) { - dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + dev_err(cs35l41->dev, "Write CS35L41_INPUT_SRC_VBSTMON failed: %d\n", ret); goto err_dsp; } ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, -- cgit v1.2.3 From cebfbc89ae2552dbb58cd9b8206a5c8e0e6301e9 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 15 Apr 2024 06:27:23 +0000 Subject: ASoC: rt715: add vendor clear control register Add vendor clear control register in readable register's callback function. This prevents an access failure reported in Intel CI tests. Signed-off-by: Jack Yu Closes: https://github.com/thesofproject/linux/issues/4860 Tested-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/6a103ce9134d49d8b3941172c87a7bd4@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 7e13868ff99f..f012fe0ded6d 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -111,6 +111,7 @@ static bool rt715_readable_register(struct device *dev, unsigned int reg) case 0x839d: case 0x83a7: case 0x83a9: + case 0x752001: case 0x752039: return true; default: -- cgit v1.2.3 From 9a039db9273b44427b3daca88173e57596545ec0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 17 Apr 2024 10:58:04 +0300 Subject: ASoC: SOF: Core: Handle error returned by sof_select_ipc_and_paths The patch which fixed the missing remove_late() calls missed a case when sof_select_ipc_and_paths() could return with error and in this case sof_init_environment() would just return with 0. Do not ignore the error code returned by sof_select_ipc_and_paths(). Fixes: 90f8917e7a15 ("ASoC: SOF: Core: Add remove_late() to sof_init_environment failure path") Signed-off-by: Peter Ujfalusi Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240417075804.10829-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index cc84d4c81be9..238bda5f6b76 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -350,7 +350,9 @@ static int sof_init_environment(struct snd_sof_dev *sdev) } ret = sof_select_ipc_and_paths(sdev); - if (!ret && plat_data->ipc_type != base_profile->ipc_type) { + if (ret) { + goto err_machine_check; + } else if (plat_data->ipc_type != base_profile->ipc_type) { /* IPC type changed, re-initialize the ops */ sof_ops_free(sdev); -- cgit v1.2.3 From 4cbb5050bffc49c716381ea2ecb07306dd46f83a Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Thu, 18 Apr 2024 16:26:21 +0200 Subject: ASoC: Intel: avs: Set name of control as in topology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When creating controls attached to widgets, there are a lot of rules if they get their name prefixed with widget name or not. Due to that controls ended up with weirdly looking names like "ssp0_fe DSP Volume", while topology set it to "DSP Volume". Fix this by setting no_wname_in_kcontrol_name to true in avs topology widgets which disables unwanted behaviour. Fixes: be2b81b519d7 ("ASoC: Intel: avs: Parse control tuples") Signed-off-by: Amadeusz Sławiński Link: https://lore.kernel.org/r/20240418142621.2487478-1-amadeuszx.slawinski@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/avs/topology.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 13061bd1488b..42b42903ae9d 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1582,6 +1582,8 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, if (!le32_to_cpu(dw->priv.size)) return 0; + w->no_wname_in_kcontrol_name = true; + if (w->ignore_suspend && !AVS_S0IX_SUPPORTED) { dev_info_once(comp->dev, "Device does not support S0IX, check BIOS settings\n"); w->ignore_suspend = false; -- cgit v1.2.3 From d18ca8635db2f88c17acbdf6412f26d4f6aff414 Mon Sep 17 00:00:00 2001 From: Joao Paulo Goncalves Date: Wed, 17 Apr 2024 15:41:38 -0300 Subject: ASoC: ti: davinci-mcasp: Fix race condition during probe When using davinci-mcasp as CPU DAI with simple-card, there are some conditions that cause simple-card to finish registering a sound card before davinci-mcasp finishes registering all sound components. This creates a non-working sound card from userspace with no problem indication apart from not being able to play/record audio on a PCM stream. The issue arises during simultaneous probe execution of both drivers. Specifically, the simple-card driver, awaiting a CPU DAI, proceeds as soon as davinci-mcasp registers its DAI. However, this process can lead to the client mutex lock (client_mutex in soc-core.c) being held or davinci-mcasp being preempted before PCM DMA registration on davinci-mcasp finishes. This situation occurs when the probes of both drivers run concurrently. Below is the code path for this condition. To solve the issue, defer davinci-mcasp CPU DAI registration to the last step in the audio part of it. This way, simple-card CPU DAI parsing will be deferred until all audio components are registered. Fail Code Path: simple-card.c: probe starts simple-card.c: simple_dai_link_of: simple_parse_node(..,cpu,..) returns EPROBE_DEFER, no CPU DAI yet davinci-mcasp.c: probe starts davinci-mcasp.c: devm_snd_soc_register_component() register CPU DAI simple-card.c: probes again, finish CPU DAI parsing and call devm_snd_soc_register_card() simple-card.c: finish probe davinci-mcasp.c: *dma_pcm_platform_register() register PCM DMA davinci-mcasp.c: probe finish Cc: stable@vger.kernel.org Fixes: 9fbd58cf4ab0 ("ASoC: davinci-mcasp: Choose PCM driver based on configured DMA controller") Signed-off-by: Joao Paulo Goncalves Acked-by: Peter Ujfalusi Reviewed-by: Jai Luthra Link: https://lore.kernel.org/r/20240417184138.1104774-1-jpaulo.silvagoncalves@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index b892d66f7847..1e760c315521 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2417,12 +2417,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp_reparent_fck(pdev); - ret = devm_snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, - &davinci_mcasp_dai[mcasp->op_mode], 1); - - if (ret != 0) - goto err; - ret = davinci_mcasp_get_dma_type(mcasp); switch (ret) { case PCM_EDMA: @@ -2449,6 +2443,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) goto err; } + ret = devm_snd_soc_register_component(&pdev->dev, &davinci_mcasp_component, + &davinci_mcasp_dai[mcasp->op_mode], 1); + + if (ret != 0) + goto err; + no_audio: ret = davinci_mcasp_init_gpiochip(mcasp); if (ret) { -- cgit v1.2.3 From 32ac501957e5f68fe0e4bf88fb4db75cfb8f6566 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 19 Apr 2024 15:00:12 +0100 Subject: ASoC: codecs: wsa881x: set clk_stop_mode1 flag WSA881x codecs do not retain the state while clock is stopped, so mark this with clk_stop_mode1 flag. Fixes: a0aab9e1404a ("ASoC: codecs: add wsa881x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20240419140012.91384-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 3c025dabaf7a..1253695bebd8 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1155,6 +1155,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + pdev->prop.clk_stop_mode1 = true; gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); -- cgit v1.2.3 From f2602fba4723e408380eb9a56e921d36a1ae21f8 Mon Sep 17 00:00:00 2001 From: Simon Trimmer Date: Mon, 22 Apr 2024 11:32:11 +0100 Subject: ASoC: cs35l56: Avoid static analysis warning of uninitialised variable Static checkers complain that the silicon_uid variable passed by pointer to cs35l56_read_silicon_uid() could later be used uninitialised when calling cs_amp_get_efi_calibration_data(). cs35l56_read_silicon_uid() must have succeeded to call cs_amp_get_efi_calibration_data() and that would have populated the variable. However, initialise the value so we are not haunted by it forevermore. Signed-off-by: Simon Trimmer Fixes: e1830f66f6c6 ("ASoC: cs35l56: Add helper functions for amp calibration") Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20240422103211.236063-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index ec1d95e57061..fd02b621da52 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -706,7 +706,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, SND_SOC_CS35L56_SHARED); int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base) { - u64 silicon_uid; + u64 silicon_uid = 0; int ret; /* Driver can't apply calibration to a secured part, so skip */ -- cgit v1.2.3 From bda16500dd0b05e2e047093b36cbe0873c95aeae Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Tue, 23 Apr 2024 06:59:35 +0000 Subject: ASoC: rt715-sdca: volume step modification Volume step (dB/step) modification to fix format error which shown in amixer control. Signed-off-by: Jack Yu Link: https://lore.kernel.org/r/b1f546ad16dc4c7abb7daa7396e8345c@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdca.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 3fb7b9adb61d..bc3579203c7a 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -316,7 +316,7 @@ static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol, return 0; } -static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); static int rt715_sdca_get_volsw(struct snd_kcontrol *kcontrol, @@ -477,7 +477,7 @@ static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = { RT715_SDCA_FU_VOL_CTRL, CH_01), SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, + 0x2f, 0x3f, 0, rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put, in_vol_tlv), RT715_SDCA_EXT_TLV("FU02 Capture Volume", @@ -485,13 +485,13 @@ static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = { RT715_SDCA_FU_VOL_CTRL, CH_01), rt715_sdca_set_amp_gain_4ch_get, rt715_sdca_set_amp_gain_4ch_put, - in_vol_tlv, 4, 0x7f), + in_vol_tlv, 4, 0x3f), RT715_SDCA_EXT_TLV("FU06 Capture Volume", SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, RT715_SDCA_FU_VOL_CTRL, CH_01), rt715_sdca_set_amp_gain_4ch_get, rt715_sdca_set_amp_gain_4ch_put, - in_vol_tlv, 4, 0x7f), + in_vol_tlv, 4, 0x3f), /* MIC Boost Control */ RT715_SDCA_BOOST_EXT_TLV("FU0E Boost", SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, -- cgit v1.2.3 From b11d26660dff8d7430892008616452dc8e5fb0f3 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:38 +0200 Subject: ASoC: meson: axg-fifo: use threaded irq to check periods With the AXG audio subsystem, there is a possible random channel shift on TDM capture, when the slot number per lane is more than 2, and there is more than one lane used. The problem has been there since the introduction of the axg audio support but such scenario is pretty uncommon. This is why there is no loud complains about the problem. Solving the problem require to make the links non-atomic and use the trigger() callback to start FEs and BEs in the appropriate order. This was tried in the past and reverted because it caused the block irq to sleep while atomic. However, instead of reverting, the solution is to call snd_pcm_period_elapsed() in a non atomic context. Use the bottom half of a threaded IRQ to do so. Fixes: 6dc4fa179fb8 ("ASoC: meson: add axg fifo base driver") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-2-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index bebee0ca8e38..ecb3eb7a9723 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -204,18 +204,26 @@ static irqreturn_t axg_fifo_pcm_irq_block(int irq, void *dev_id) unsigned int status; regmap_read(fifo->map, FIFO_STATUS1, &status); - status = FIELD_GET(STATUS1_INT_STS, status); + axg_fifo_ack_irq(fifo, status); + + /* Use the thread to call period elapsed on nonatomic links */ if (status & FIFO_INT_COUNT_REPEAT) - snd_pcm_period_elapsed(ss); - else - dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", - status); + return IRQ_WAKE_THREAD; - /* Ack irqs */ - axg_fifo_ack_irq(fifo, status); + dev_dbg(axg_fifo_dev(ss), "unexpected irq - STS 0x%02x\n", + status); + + return IRQ_NONE; +} + +static irqreturn_t axg_fifo_pcm_irq_block_thread(int irq, void *dev_id) +{ + struct snd_pcm_substream *ss = dev_id; + + snd_pcm_period_elapsed(ss); - return IRQ_RETVAL(status); + return IRQ_HANDLED; } int axg_fifo_pcm_open(struct snd_soc_component *component, @@ -243,8 +251,9 @@ int axg_fifo_pcm_open(struct snd_soc_component *component, if (ret) return ret; - ret = request_irq(fifo->irq, axg_fifo_pcm_irq_block, 0, - dev_name(dev), ss); + ret = request_threaded_irq(fifo->irq, axg_fifo_pcm_irq_block, + axg_fifo_pcm_irq_block_thread, + IRQF_ONESHOT, dev_name(dev), ss); if (ret) return ret; -- cgit v1.2.3 From dcba52ace7d4c12e2c8c273eff55ea03a84c8baf Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:39 +0200 Subject: ASoC: meson: axg-card: make links nonatomic Non atomic operations need to be performed in the trigger callback of the TDM interfaces. Those are BEs but what matters is the nonatomic flag of the FE in the DPCM context. Just set nonatomic for everything so, at least, what is done is clear. Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-3-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-card.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index 3180aa4d3a15..8c5605c1e34e 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -318,6 +318,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; + dai_link->nonatomic = true; ret = meson_card_parse_dai(card, np, dai_link->cpus); if (ret) -- cgit v1.2.3 From f949ed458ad15a00d41b37c745ebadaef171aaae Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:40 +0200 Subject: ASoC: meson: axg-tdm-interface: manage formatters in trigger So far, the formatters have been reset/enabled using the .prepare() callback. This was done in this callback because walking the formatters use a mutex. A mutex is used because formatter handling require dealing possibly slow clock operation. With the support of non-atomic, .trigger() callback may be used which also allows to properly enable and disable formatters on start but also pause/resume. This solve a random shift on TDMIN as well repeated samples on for TDMOUT. Fixes: d60e4f1e4be5 ("ASoC: meson: add tdm interface driver") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-4-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-interface.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index bf708717635b..8bf3735dedaa 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -349,26 +349,31 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, return 0; } -static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, +static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { - struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct axg_tdm_stream *ts = + snd_soc_dai_get_dma_data(dai, substream); - /* Stop all attached formatters */ - axg_tdm_stream_stop(ts); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + axg_tdm_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + axg_tdm_stream_stop(ts); + break; + default: + return -EINVAL; + } return 0; } -static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); - - /* Force all attached formatters to update */ - return axg_tdm_stream_reset(ts); -} - static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) { int stream; @@ -412,8 +417,7 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = { .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, .hw_params = axg_tdm_iface_hw_params, - .prepare = axg_tdm_iface_prepare, - .hw_free = axg_tdm_iface_hw_free, + .trigger = axg_tdm_iface_trigger, }; /* TDM Backend DAIs */ -- cgit v1.2.3 From a5a89037d080e0870d7517c61f8b2123d58ab33b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 17:29:41 +0200 Subject: ASoC: meson: axg-tdm: add continuous clock support Some devices may need the clocks running, even while paused. Add support for this use case. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426152946.3078805-5-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-tdm-formatter.c | 40 +++++++++++++++++++++++++++++++++++++ sound/soc/meson/axg-tdm-interface.c | 16 ++++++++++++++- sound/soc/meson/axg-tdm.h | 5 +++++ 3 files changed, 60 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index 63333a2b0a9c..a6579efd3775 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -392,6 +392,46 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts) } EXPORT_SYMBOL_GPL(axg_tdm_stream_free); +int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts, + unsigned int fmt) +{ + int ret = 0; + + if (fmt & SND_SOC_DAIFMT_CONT) { + /* Clock are already enabled - skipping */ + if (ts->clk_enabled) + return 0; + + ret = clk_prepare_enable(ts->iface->mclk); + if (ret) + return ret; + + ret = clk_prepare_enable(ts->iface->sclk); + if (ret) + goto err_sclk; + + ret = clk_prepare_enable(ts->iface->lrclk); + if (ret) + goto err_lrclk; + + ts->clk_enabled = true; + return 0; + } + + /* Clocks are already disabled - skipping */ + if (!ts->clk_enabled) + return 0; + + clk_disable_unprepare(ts->iface->lrclk); +err_lrclk: + clk_disable_unprepare(ts->iface->sclk); +err_sclk: + clk_disable_unprepare(ts->iface->mclk); + ts->clk_enabled = false; + return ret; +} +EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks); + MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver"); MODULE_AUTHOR("Jerome Brunet "); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 8bf3735dedaa..62057c71f742 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -309,6 +309,7 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); int ret; switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -346,7 +347,19 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, return ret; } - return 0; + ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt); + if (ret) + dev_err(dai->dev, "failed to apply continuous clock setting\n"); + + return ret; +} + +static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + + return axg_tdm_stream_set_cont_clocks(ts, 0); } static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream, @@ -417,6 +430,7 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = { .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, .hw_params = axg_tdm_iface_hw_params, + .hw_free = axg_tdm_iface_hw_free, .trigger = axg_tdm_iface_trigger, }; diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h index 42f7470b9a7f..daaca10fec9e 100644 --- a/sound/soc/meson/axg-tdm.h +++ b/sound/soc/meson/axg-tdm.h @@ -58,12 +58,17 @@ struct axg_tdm_stream { unsigned int physical_width; u32 *mask; bool ready; + + /* For continuous clock tracking */ + bool clk_enabled; }; struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface); void axg_tdm_stream_free(struct axg_tdm_stream *ts); int axg_tdm_stream_start(struct axg_tdm_stream *ts); void axg_tdm_stream_stop(struct axg_tdm_stream *ts); +int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts, + unsigned int fmt); static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts) { -- cgit v1.2.3 From 6db26f9ea4edd8a17d39ab3c20111e3ccd704aef Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 26 Apr 2024 15:41:47 +0200 Subject: ASoC: meson: cards: select SND_DYNAMIC_MINORS Amlogic sound cards do create a lot of pcm interfaces, possibly more than 8. Some pcm interfaces are internal (like DPCM backends and c2c) and not exposed to userspace. Those interfaces still increase the number passed to snd_find_free_minor(), which eventually exceeds 8 causing -EBUSY error on card registration if CONFIG_SND_DYNAMIC_MINORS=n and the interface is exposed to userspace. select CONFIG_SND_DYNAMIC_MINORS for Amlogic cards to avoid the problem. Fixes: 7864a79f37b5 ("ASoC: meson: add axg sound card support") Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240426134150.3053741-1-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index b93ea33739f2..6458d5dc4902 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -99,6 +99,7 @@ config SND_MESON_AXG_PDM config SND_MESON_CARD_UTILS tristate + select SND_DYNAMIC_MINORS config SND_MESON_CODEC_GLUE tristate -- cgit v1.2.3 From e8a6a5ad73acbafd98e8fd3f0cbf6e379771bb76 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:30:33 -0500 Subject: ASoC: da7219-aad: fix usage of device_get_named_child_node() The documentation for device_get_named_child_node() mentions this important point: " The caller is responsible for calling fwnode_handle_put() on the returned fwnode pointer. " Add fwnode_handle_put() to avoid a leaked reference. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240426153033.38500-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219-aad.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 6bc068cdcbe2..15e5e3eb592b 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -671,8 +671,10 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) return NULL; aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL); - if (!aad_pdata) + if (!aad_pdata) { + fwnode_handle_put(aad_np); return NULL; + } aad_pdata->irq = i2c->irq; @@ -753,6 +755,8 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) else aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + fwnode_handle_put(aad_np); + return aad_pdata; } -- cgit v1.2.3 From fbd741f0993203d07b2b6562d68d1e5e4745b59b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 26 Apr 2024 10:29:39 -0500 Subject: ASoC: cs35l56: fix usages of device_get_named_child_node() The documentation for device_get_named_child_node() mentions this important point: " The caller is responsible for calling fwnode_handle_put() on the returned fwnode pointer. " Add fwnode_handle_put() to avoid leaked references. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20240426152939.38471-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 6331b8c6136e..4986e78105da 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1360,6 +1360,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj); if (ret) { dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret); + fwnode_handle_put(af01_fwnode); return -ENOENT; } @@ -1367,6 +1368,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 if (obj->package.count != 4) { dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n", obj->package.count); + fwnode_handle_put(af01_fwnode); return -ENOENT; } @@ -1381,6 +1383,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 */ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping); if (ret) { + fwnode_handle_put(af01_fwnode); return dev_err_probe(cs35l56->base.dev, ret, "Failed to add gpio mapping to AF01\n"); } @@ -1388,14 +1391,17 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 ret = devm_add_action_or_reset(cs35l56->base.dev, cs35l56_acpi_dev_release_driver_gpios, adev); - if (ret) + if (ret) { + fwnode_handle_put(af01_fwnode); return ret; + } dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n"); } desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL); if (IS_ERR(desc)) { + fwnode_handle_put(af01_fwnode); ret = PTR_ERR(desc); return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n"); } @@ -1404,9 +1410,12 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5 gpiod_put(desc); if (ret < 0) { + fwnode_handle_put(af01_fwnode); dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n"); return ret; - } + } + + fwnode_handle_put(af01_fwnode); dev_info(cs35l56->base.dev, "Got spk-id from AF01\n"); -- cgit v1.2.3 From a4ffa8b2fc0ebb86d001f1efb552387c97a2507d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 26 Mar 2024 11:04:19 -0500 Subject: ASoC: Intel: sof_sdw: use generic rtd_init function for Realtek SDW DMICs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit bee2fe44679f1e6a5332d7f78587ccca4109919f upstream. The only thing that the rt_xxx_rtd_init() functions do is to set card->components. And we can set card->components with name_prefix as rt712_sdca_dmic_rtd_init() does. And sof_sdw_rtd_init() will always select the first dai with the given dai->name from codec_info_list[]. Unfortunately, we have different codecs with the same dai name. For example, dai name of rt715 and rt715-sdca are both "rt715-aif2". Using a generic rtd_init allow sof_sdw_rtd_init() run the rtd_init() callback from a similar codec dai. Fixes: 8266c73126b7 ("ASoC: Intel: sof_sdw: add common sdw dai link init") Reviewed-by: Chao Song Reviewed-by: Péter Ujfalusi Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240326160429.13560-25-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Peter Ujfalusi Signed-off-by: Greg Kroah-Hartman --- sound/soc/intel/boards/Makefile | 1 + sound/soc/intel/boards/sof_sdw.c | 12 ++++---- sound/soc/intel/boards/sof_sdw_common.h | 1 + sound/soc/intel/boards/sof_sdw_rt_dmic.c | 52 ++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 sound/soc/intel/boards/sof_sdw_rt_dmic.c (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index bbf796a5f7ba..08cfd4baecdd 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -42,6 +42,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \ sof_sdw_rt712_sdca.o sof_sdw_rt715.o \ sof_sdw_rt715_sdca.o sof_sdw_rt722_sdca.o \ + sof_sdw_rt_dmic.o \ sof_sdw_cs42l42.o sof_sdw_cs42l43.o \ sof_sdw_cs_amp.o \ sof_sdw_dmic.o \ diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 08f330ed5c2e..a90b43162a54 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -730,7 +730,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt712_sdca_dmic_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, @@ -760,7 +760,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt712-sdca-dmic-aif1", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt712_sdca_dmic_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, @@ -822,7 +822,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt715_sdca_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, @@ -837,7 +837,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt715_sdca_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, @@ -852,7 +852,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt715_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, @@ -867,7 +867,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt715-aif2", .dai_type = SOF_SDW_DAI_TYPE_MIC, .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, - .rtd_init = rt715_rtd_init, + .rtd_init = rt_dmic_rtd_init, }, }, .dai_num = 1, diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index b1d57034361c..8a541b6bb0ac 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -190,6 +190,7 @@ int rt712_sdca_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); int rt712_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); int rt715_rtd_init(struct snd_soc_pcm_runtime *rtd); int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd); +int rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd); int rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd); int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd); diff --git a/sound/soc/intel/boards/sof_sdw_rt_dmic.c b/sound/soc/intel/boards/sof_sdw_rt_dmic.c new file mode 100644 index 000000000000..9091f5b5c648 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt_dmic.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2024 Intel Corporation + +/* + * sof_sdw_rt_dmic - Helpers to handle Realtek SDW DMIC from generic machine driver + */ + +#include +#include +#include +#include +#include "sof_board_helpers.h" +#include "sof_sdw_common.h" + +static const char * const dmics[] = { + "rt715", + "rt712-sdca-dmic", +}; + +int rt_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_component *component; + struct snd_soc_dai *codec_dai; + char *mic_name; + + codec_dai = get_codec_dai_by_name(rtd, dmics, ARRAY_SIZE(dmics)); + if (!codec_dai) + return -EINVAL; + + component = codec_dai->component; + + /* + * rt715-sdca (aka rt714) is a special case that uses different name in card->components + * and component->name_prefix. + */ + if (!strcmp(component->name_prefix, "rt714")) + mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "rt715-sdca"); + else + mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s", component->name_prefix); + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:%s", card->components, + mic_name); + if (!card->components) + return -ENOMEM; + + dev_dbg(card->dev, "card->components: %s\n", card->components); + + return 0; +} +MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); -- cgit v1.2.3 From 214c70c0ad5ab49d3a70da219cfba7a646f0a863 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 8 Mar 2024 10:04:58 +0100 Subject: ASoC: Intel: Disable route checks for Skylake boards [ Upstream commit 0cb3b7fd530b8c107443218ce6db5cb6e7b5dbe1 ] Topology files that are propagated to the world and utilized by the skylake-driver carry shortcomings in their SectionGraphs. Since commit daa480bde6b3 ("ASoC: soc-core: tidyup for snd_soc_dapm_add_routes()") route checks are no longer permissive. Probe failures for Intel boards have been partially addressed by commit a22ae72b86a4 ("ASoC: soc-core: disable route checks for legacy devices") and its follow up but only skl_nau88l25_ssm4567.c is patched. Fix the problem for the rest of the boards. Link: https://lore.kernel.org/all/20200309192744.18380-1-pierre-louis.bossart@linux.intel.com/ Fixes: daa480bde6b3 ("ASoC: soc-core: tidyup for snd_soc_dapm_add_routes()") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240308090502.2136760-2-cezary.rojewski@intel.com Reviewed-by: Pierre-Louis Bossart Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/boards/bxt_da7219_max98357a.c | 1 + sound/soc/intel/boards/bxt_rt298.c | 1 + sound/soc/intel/boards/glk_rt5682_max98357a.c | 2 ++ sound/soc/intel/boards/kbl_da7219_max98357a.c | 1 + sound/soc/intel/boards/kbl_da7219_max98927.c | 4 ++++ sound/soc/intel/boards/kbl_rt5660.c | 1 + sound/soc/intel/boards/kbl_rt5663_max98927.c | 2 ++ sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 1 + sound/soc/intel/boards/skl_hda_dsp_generic.c | 2 ++ sound/soc/intel/boards/skl_nau88l25_max98357a.c | 1 + sound/soc/intel/boards/skl_rt286.c | 1 + 11 files changed, 17 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 540f7a29310a..3fe3f38c6cb6 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -768,6 +768,7 @@ static struct snd_soc_card broxton_audio_card = { .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = bxt_card_late_probe, }; diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index c0eb65c14aa9..afc499be8db2 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -574,6 +574,7 @@ static struct snd_soc_card broxton_rt298 = { .dapm_routes = broxton_rt298_map, .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = bxt_card_late_probe, }; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index 657e4658234c..4098b2d32f9b 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -649,6 +649,8 @@ static int geminilake_audio_probe(struct platform_device *pdev) card = &glk_audio_card_rt5682_m98357a; card->dev = &pdev->dev; snd_soc_card_set_drvdata(card, ctx); + if (!snd_soc_acpi_sof_parent(&pdev->dev)) + card->disable_route_checks = true; /* override platform name, if required */ mach = pdev->dev.platform_data; diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index a5d8965303a8..9dbc15f9d1c9 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -639,6 +639,7 @@ static struct snd_soc_card kabylake_audio_card_da7219_m98357a = { .dapm_routes = kabylake_map, .num_dapm_routes = ARRAY_SIZE(kabylake_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 98c11ec0adc0..e662da5af83b 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -1036,6 +1036,7 @@ static struct snd_soc_card kbl_audio_card_da7219_m98927 = { .codec_conf = max98927_codec_conf, .num_configs = ARRAY_SIZE(max98927_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; @@ -1054,6 +1055,7 @@ static struct snd_soc_card kbl_audio_card_max98927 = { .codec_conf = max98927_codec_conf, .num_configs = ARRAY_SIZE(max98927_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; @@ -1071,6 +1073,7 @@ static struct snd_soc_card kbl_audio_card_da7219_m98373 = { .codec_conf = max98373_codec_conf, .num_configs = ARRAY_SIZE(max98373_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; @@ -1088,6 +1091,7 @@ static struct snd_soc_card kbl_audio_card_max98373 = { .codec_conf = max98373_codec_conf, .num_configs = ARRAY_SIZE(max98373_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index 30e0aca161cd..894d127c482a 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -518,6 +518,7 @@ static struct snd_soc_card kabylake_audio_card_rt5660 = { .dapm_routes = kabylake_rt5660_map, .num_dapm_routes = ARRAY_SIZE(kabylake_rt5660_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 9071b1f1cbd0..646e8ff8e961 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -966,6 +966,7 @@ static struct snd_soc_card kabylake_audio_card_rt5663_m98927 = { .codec_conf = max98927_codec_conf, .num_configs = ARRAY_SIZE(max98927_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; @@ -982,6 +983,7 @@ static struct snd_soc_card kabylake_audio_card_rt5663 = { .dapm_routes = kabylake_5663_map, .num_dapm_routes = ARRAY_SIZE(kabylake_5663_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 178fe9c37df6..924d5d1de03a 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -791,6 +791,7 @@ static struct snd_soc_card kabylake_audio_card = { .codec_conf = max98927_codec_conf, .num_configs = ARRAY_SIZE(max98927_codec_conf), .fully_routed = true, + .disable_route_checks = true, .late_probe = kabylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 6e172719c979..4aa7fd2a05e4 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -227,6 +227,8 @@ static int skl_hda_audio_probe(struct platform_device *pdev) ctx->common_hdmi_codec_drv = mach->mach_params.common_hdmi_codec_drv; hda_soc_card.dev = &pdev->dev; + if (!snd_soc_acpi_sof_parent(&pdev->dev)) + hda_soc_card.disable_route_checks = true; if (mach->mach_params.dmic_num > 0) { snprintf(hda_soc_components, sizeof(hda_soc_components), diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 0e7025834594..e4630c33176e 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -654,6 +654,7 @@ static struct snd_soc_card skylake_audio_card = { .dapm_routes = skylake_map, .num_dapm_routes = ARRAY_SIZE(skylake_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = skylake_card_late_probe, }; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index c59c60e28091..9a8044274908 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -523,6 +523,7 @@ static struct snd_soc_card skylake_rt286 = { .dapm_routes = skylake_rt286_map, .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), .fully_routed = true, + .disable_route_checks = true, .late_probe = skylake_card_late_probe, }; -- cgit v1.2.3 From 308bff2299947a1b611166d972f339e2a228d138 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 8 Mar 2024 10:05:00 +0100 Subject: ASoC: Intel: avs: ssm4567: Do not ignore route checks [ Upstream commit e6719d48ba6329536c459dcee5a571e535687094 ] A copy-paste from intel/boards/skl_nau88l25_ssm4567.c made the avs's equivalent disable route checks as well. Such behavior is not desired. Fixes: 69ea14efe99b ("ASoC: Intel: avs: Add ssm4567 machine board") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240308090502.2136760-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/boards/ssm4567.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index d6f7f046c24e..f634261e4f60 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -172,7 +172,6 @@ static int avs_ssm4567_probe(struct platform_device *pdev) card->dapm_routes = card_base_routes; card->num_dapm_routes = ARRAY_SIZE(card_base_routes); card->fully_routed = true; - card->disable_route_checks = true; ret = snd_soc_fixup_dai_links_platform_name(card, pname); if (ret) -- cgit v1.2.3 From 0c052b1c11d8119f3048b1f7b3c39a90500cacf9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Mar 2024 12:01:29 +0100 Subject: ASoC: mediatek: Assign dummy when codec not specified for a DAI link [ Upstream commit 5f39231888c63f0a7708abc86b51b847476379d8 ] MediaTek sound card drivers are checking whether a DAI link is present and used on a board to assign the correct parameters and this is done by checking the codec DAI names at probe time. If no real codec is present, assign the dummy codec to the DAI link to avoid NULL pointer during string comparison. Fixes: 4302187d955f ("ASoC: mediatek: common: add soundcard driver common code") Signed-off-by: AngeloGioacchino Del Regno Link: https://msgid.link/r/20240313110147.1267793-5-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/mediatek/common/mtk-soundcard-driver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c index a58e1e3674de..000a086a8cf4 100644 --- a/sound/soc/mediatek/common/mtk-soundcard-driver.c +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -22,7 +22,11 @@ static int set_card_codec_info(struct snd_soc_card *card, codec_node = of_get_child_by_name(sub_node, "codec"); if (!codec_node) { - dev_dbg(dev, "%s no specified codec\n", dai_link->name); + dev_dbg(dev, "%s no specified codec: setting dummy.\n", dai_link->name); + + dai_link->codecs = &snd_soc_dummy_dlc; + dai_link->num_codecs = 1; + dai_link->dynamic = 1; return 0; } -- cgit v1.2.3 From 802b49e39da669b54bd9b77dc3c649999a446bf6 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Thu, 28 Mar 2024 20:33:37 +0300 Subject: ASoC: kirkwood: Fix potential NULL dereference [ Upstream commit ea60ab95723f5738e7737b56dda95e6feefa5b50 ] In kirkwood_dma_hw_params() mv_mbus_dram_info() returns NULL if CONFIG_PLAT_ORION macro is not defined. Fix this bug by adding NULL check. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: bb6a40fc5a83 ("ASoC: kirkwood: Fix reference to PCM buffer address") Signed-off-by: Aleksandr Mishin Link: https://msgid.link/r/20240328173337.21406-1-amishin@t-argos.ru Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/kirkwood/kirkwood-dma.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index dd2f806526c1..ef00792e1d49 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -182,6 +182,9 @@ static int kirkwood_dma_hw_params(struct snd_soc_component *component, const struct mbus_dram_target_info *dram = mv_mbus_dram_info(); unsigned long addr = substream->runtime->dma_addr; + if (!dram) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_PLAYBACK_WIN, addr, dram); -- cgit v1.2.3 From 64028347af70a776c65e12fa6e861561559c870b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 2 Apr 2024 10:18:12 -0500 Subject: ASoC: SOF: Intel: hda-dai: fix channel map configuration for aggregated dailink [ Upstream commit 831045513c8a2ef14c3cf39b33d1ccedf588c4a8 ] The existing code derives the channel map used to program the HDaudio link DMA from the hw_params, but that is not quite right in the case of aggregation. The code in soc-pcm.c splits the hw_params depending on the codec_ch_map, and we need to reconstruct the channel-map to insert the data in the right places. This issue is seen only on amplifier feedback capture where the data from the second amplifier was replaced by that of the first amplifier. Note that the loop iterator of the macro for_each_rtd_cpu_dais() is reused in a following loop. This is different to all existing usages of that macro, hence the use of a boolean flag to avoid an access to an uninitialized variable. Fixes: 2960ee5c4814 ("ASoC: SOF: Intel: hda-dai: add helpers for SoundWire callbacks") Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://msgid.link/r/20240402151828.175002-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/hda-dai.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c1682bcdb5a6..6a39ca632f55 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -439,10 +439,17 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, int link_id) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); const struct hda_dai_widget_dma_ops *ops; + struct snd_soc_dai_link_ch_map *ch_maps; struct hdac_ext_stream *hext_stream; + struct snd_soc_dai *dai; struct snd_sof_dev *sdev; + bool cpu_dai_found = false; + int cpu_dai_id; + int ch_mask; int ret; + int j; ret = non_hda_dai_hw_params(substream, params, cpu_dai); if (ret < 0) { @@ -457,9 +464,29 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) return -ENODEV; - /* in the case of SoundWire we need to program the PCMSyCM registers */ + /* + * in the case of SoundWire we need to program the PCMSyCM registers. In case + * of aggregated devices, we need to define the channel mask for each sublink + * by reconstructing the split done in soc-pcm.c + */ + for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { + if (dai == cpu_dai) { + cpu_dai_found = true; + break; + } + } + + if (!cpu_dai_found) + return -ENODEV; + + ch_mask = 0; + for_each_link_ch_maps(rtd->dai_link, j, ch_maps) { + if (ch_maps->cpu == cpu_dai_id) + ch_mask |= ch_maps->ch_mask; + } + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, - GENMASK(params_channels(params) - 1, 0), + ch_mask, hdac_stream(hext_stream)->stream_tag, substream->stream); if (ret < 0) { -- cgit v1.2.3 From f25539ac3a6a3b151daafee6beed5fffe18907c2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Apr 2024 13:52:05 +0300 Subject: ASoC: SOF: Intel: mtl: Correct rom_status_reg [ Upstream commit 1f1b820dc3c65b6883da3130ba3b8624dcbf87db ] ACE1 architecture changed the place where the ROM updates the status code from the shared SRAM window to HFFLGP1QW0 register for the status and HFFLGP1QW0 + 4 for the error code. The rom_status_reg is not used on MTL because it was wrongly assigned based on older platform convention (SRAM window) and it was giving inconsistent readings. Fixes: 064520e8aeaa ("ASoC: SOF: Intel: Add support for MeteorLake (MTL)") Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Liam Girdwood Link: https://msgid.link/r/20240403105210.17949-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/mtl.c | 4 ++-- sound/soc/sof/intel/mtl.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 060c34988e90..1454e2a98c3b 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -727,7 +727,7 @@ const struct sof_intel_dsp_desc mtl_chip_info = { .ipc_ack = MTL_DSP_REG_HFIPCXIDA, .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, - .rom_status_reg = MTL_DSP_ROM_STS, + .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY, .rom_init_timeout = 300, .ssp_count = MTL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, @@ -755,7 +755,7 @@ const struct sof_intel_dsp_desc arl_s_chip_info = { .ipc_ack = MTL_DSP_REG_HFIPCXIDA, .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, - .rom_status_reg = MTL_DSP_ROM_STS, + .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY, .rom_init_timeout = 300, .ssp_count = MTL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index ea8c1b83f712..3c56427a966b 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -70,8 +70,8 @@ #define MTL_DSP_ROM_STS MTL_SRAM_WINDOW_OFFSET(0) /* ROM status */ #define MTL_DSP_ROM_ERROR (MTL_SRAM_WINDOW_OFFSET(0) + 0x4) /* ROM error code */ -#define MTL_DSP_REG_HFFLGPXQWY 0x163200 /* ROM debug status */ -#define MTL_DSP_REG_HFFLGPXQWY_ERROR 0x163204 /* ROM debug error code */ +#define MTL_DSP_REG_HFFLGPXQWY 0x163200 /* DSP core0 status */ +#define MTL_DSP_REG_HFFLGPXQWY_ERROR 0x163204 /* DSP core0 error */ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) -- cgit v1.2.3 From 0f925309d75d3625c309f47ae45be991f63dd20f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Apr 2024 13:52:06 +0300 Subject: ASoC: SOF: Intel: lnl: Correct rom_status_reg [ Upstream commit b852574c671a9983dd51c81582c8c5085f3dc382 ] ACE2 architecture changed the place where the ROM updates the status code from the shared SRAM window (and HFFLGP1QW0 in ACE1) to HFDSC register for the status and HFDEC (HFDSC + 4) for the error code. The rom_status_reg is not used on LNL because it was wrongly assigned based on older platform convention (SRAM window) and it was giving inconsistent readings. Add new header file for lnl specific register definitions. Fixes: 64a63d9914a5 ("ASoC: SOF: Intel: LNL: Add support for Lunarlake platform") Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Liam Girdwood Link: https://msgid.link/r/20240403105210.17949-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/lnl.c | 3 ++- sound/soc/sof/intel/lnl.h | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/intel/lnl.h (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index aeb4350cce6b..6055a33bb4bf 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -16,6 +16,7 @@ #include "hda-ipc.h" #include "../sof-audio.h" #include "mtl.h" +#include "lnl.h" #include /* LunarLake ops */ @@ -208,7 +209,7 @@ const struct sof_intel_dsp_desc lnl_chip_info = { .ipc_ack = MTL_DSP_REG_HFIPCXIDA, .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, - .rom_status_reg = MTL_DSP_ROM_STS, + .rom_status_reg = LNL_DSP_REG_HFDSC, .rom_init_timeout = 300, .ssp_count = MTL_SSP_COUNT, .d0i3_offset = MTL_HDA_VS_D0I3C, diff --git a/sound/soc/sof/intel/lnl.h b/sound/soc/sof/intel/lnl.h new file mode 100644 index 000000000000..4f4734fe7e08 --- /dev/null +++ b/sound/soc/sof/intel/lnl.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2024 Intel Corporation. All rights reserved. + */ + +#ifndef __SOF_INTEL_LNL_H +#define __SOF_INTEL_LNL_H + +#define LNL_DSP_REG_HFDSC 0x160200 /* DSP core0 status */ +#define LNL_DSP_REG_HFDEC 0x160204 /* DSP core0 error */ + +#endif /* __SOF_INTEL_LNL_H */ -- cgit v1.2.3 From f7eacef75767d380c1b7936d5a43f41fd041e5c2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Apr 2024 13:52:07 +0300 Subject: ASoC: SOF: Intel: mtl: Disable interrupts when firmware boot failed [ Upstream commit 26187f44aabdf3df7609b7c78724a059c230a2ad ] In case of error during the firmware boot we need to disable the interrupts which were enabled as part of the boot sequence. Fixes: 064520e8aeaa ("ASoC: SOF: Intel: Add support for MeteorLake (MTL)") Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Liam Girdwood Link: https://msgid.link/r/20240403105210.17949-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/mtl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 1454e2a98c3b..fbd7cf77e817 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -503,6 +503,7 @@ err: dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); + mtl_enable_interrupts(sdev, false); mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE); kfree(dump_msg); -- cgit v1.2.3 From fae7c3d7ae8b64ccf761164b611b669ffc95478b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Apr 2024 13:52:08 +0300 Subject: ASoC: SOF: Intel: mtl: Implement firmware boot state check [ Upstream commit 6b1c1c47e76f0161bda2b1ac2e86a219fe70244f ] With the corrected rom_status_reg values we can now add a check for target boot status for firmware booting. With the check now we can identify failed firmware boots (IMR boots) and we can use the fallback to purge boot the DSP. Fixes: 064520e8aeaa ("ASoC: SOF: Intel: Add support for MeteorLake (MTL)") Signed-off-by: Peter Ujfalusi Reviewed-by: Rander Wang Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Liam Girdwood Link: https://msgid.link/r/20240403105210.17949-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/intel/mtl.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index fbd7cf77e817..05023763080d 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -439,7 +439,7 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; - unsigned int status; + unsigned int status, target_status; u32 ipc_hdr, flags; char *dump_msg; int ret; @@ -485,13 +485,40 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) mtl_enable_ipc_interrupts(sdev); + if (chip->rom_status_reg == MTL_DSP_ROM_STS) { + /* + * Workaround: when the ROM status register is pointing to + * the SRAM window (MTL_DSP_ROM_STS) the platform cannot catch + * ROM_INIT_DONE because of a very short timing window. + * Follow the recommendations and skip target state waiting. + */ + return 0; + } + /* - * ACE workaround: don't wait for ROM INIT. - * The platform cannot catch ROM_INIT_DONE because of a very short - * timing window. Follow the recommendations and skip this part. + * step 7: + * - Cold/Full boot: wait for ROM init to proceed to download the firmware + * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR) */ + if (imr_boot) + target_status = FSR_STATE_FW_ENTERED; + else + target_status = FSR_STATE_INIT_DONE; - return 0; + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, + chip->rom_status_reg, status, + (FSR_TO_STATE_CODE(status) == target_status), + HDA_DSP_REG_POLL_INTERVAL_US, + chip->rom_init_timeout * + USEC_PER_MSEC); + + if (!ret) + return 0; + + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, + "%s: timeout with rom_status_reg (%#x) read\n", + __func__, chip->rom_status_reg); err: flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL; -- cgit v1.2.3 From b33040a3b898e32ea6543fa5c1e4d5a2eb0f85e5 Mon Sep 17 00:00:00 2001 From: Amadeusz Sławiński Date: Fri, 5 Apr 2024 11:09:17 +0200 Subject: ASoC: Intel: avs: Restore stream decoupling on prepare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 680507581e025d16a0b6d3782603ca8c598fbe2b ] Revert changes from commit b87b8f43afd5 ("ASoC: Intel: avs: Drop superfluous stream decoupling") to restore working streaming during S3. Fixes: b87b8f43afd5 ("ASoC: Intel: avs: Drop superfluous stream decoupling") Signed-off-by: Amadeusz Sławiński Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240405090929.1184068-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 2cafbc392cdb..72f1bc3b7b1f 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -356,6 +356,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn stream_info->sig_bits); format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate); + snd_hdac_ext_stream_decouple(bus, link_stream, true); snd_hdac_ext_stream_reset(link_stream); snd_hdac_ext_stream_setup(link_stream, format_val); @@ -611,6 +612,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so struct avs_dev *adev = to_avs_dev(dai->dev); struct hdac_ext_stream *host_stream; unsigned int format_val; + struct hdac_bus *bus; unsigned int bits; int ret; @@ -620,6 +622,8 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so if (hdac_stream(host_stream)->prepared) return 0; + bus = hdac_stream(host_stream)->bus; + snd_hdac_ext_stream_decouple(bus, data->host_stream, true); snd_hdac_stream_reset(hdac_stream(host_stream)); stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream); -- cgit v1.2.3 From 125962549c4fb63dc8ff04e464e61d0f03b7f071 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 5 Apr 2024 11:09:18 +0200 Subject: ASoC: Intel: avs: Fix debug-slot offset calculation [ Upstream commit c91b692781c1839fcc389b2a9120e46593c6424b ] For resources with ID other than 0 the current calculus is incorrect. Fixes: 275b583d047a ("ASoC: Intel: avs: ICL-based platforms support") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240405090929.1184068-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/icl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/icl.c b/sound/soc/intel/avs/icl.c index d2554c857732..284d38f3b1ca 100644 --- a/sound/soc/intel/avs/icl.c +++ b/sound/soc/intel/avs/icl.c @@ -66,7 +66,7 @@ struct avs_icl_memwnd2 { struct avs_icl_memwnd2_desc slot_desc[AVS_ICL_MEMWND2_SLOTS_COUNT]; u8 rsvd[SZ_4K]; }; - u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][PAGE_SIZE]; + u8 slot_array[AVS_ICL_MEMWND2_SLOTS_COUNT][SZ_4K]; } __packed; #define AVS_ICL_SLOT_UNUSED \ @@ -89,8 +89,7 @@ static int avs_icl_slot_offset(struct avs_dev *adev, union avs_icl_memwnd2_slot_ for (i = 0; i < AVS_ICL_MEMWND2_SLOTS_COUNT; i++) if (desc[i].slot_id.val == slot_type.val) - return offsetof(struct avs_icl_memwnd2, slot_array) + - avs_skl_log_buffer_offset(adev, i); + return offsetof(struct avs_icl_memwnd2, slot_array) + i * SZ_4K; return -ENXIO; } -- cgit v1.2.3 From 70d058d60aa7db69a010bd7c55beb69d23cae658 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 5 Apr 2024 11:09:21 +0200 Subject: ASoC: Intel: avs: Fix ASRC module initialization [ Upstream commit 9d2e26f31c7cc3fa495c423af9b4902ec0dc7be3 ] The ASRC module configuration consists of several reserved fields. Zero them out when initializing the module to avoid sending invalid data. Fixes: 274d79e51875 ("ASoC: Intel: avs: Configure modules according to their type") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240405090929.1184068-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/path.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index e785fc2a7008..a44ed33b5608 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -367,6 +367,7 @@ static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod) struct avs_tplg_module *t = mod->template; struct avs_asrc_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); cfg.base.cpc = t->cfg_base->cpc; cfg.base.ibs = t->cfg_base->ibs; cfg.base.obs = t->cfg_base->obs; -- cgit v1.2.3 From 3c2521a3c16f019b3d616e1fea70fb887c8233d9 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 5 Apr 2024 11:09:23 +0200 Subject: ASoC: Intel: avs: Fix potential integer overflow [ Upstream commit c7e832cabe635df47c2bf6df7801e97bf3045b1e ] While stream_tag for CLDMA on SKL-based platforms is always 1, function hda_cldma_setup() uses AZX_SD_CTL_STRM() macro which does: stream_tag << 20 what combined with stream_tag type of 'unsigned int' generates a potential overflow issue. Update the field type to fix that. Fixes: 45864e49a05a ("ASoC: Intel: avs: Implement CLDMA transfer") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240405090929.1184068-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/cldma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c index d7a9390b5e48..585579840b64 100644 --- a/sound/soc/intel/avs/cldma.c +++ b/sound/soc/intel/avs/cldma.c @@ -35,7 +35,7 @@ struct hda_cldma { unsigned int buffer_size; unsigned int num_periods; - unsigned int stream_tag; + unsigned char stream_tag; void __iomem *sd_addr; struct snd_dma_buffer dmab_data; -- cgit v1.2.3 From 252c05efeab8c563a5f6ccc8b9faa234b0d5f5a2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Fri, 5 Apr 2024 11:09:24 +0200 Subject: ASoC: Intel: avs: Test result of avs_get_module_entry() [ Upstream commit 41bf4525fadb3d8df3860420d6ac9025c51a3bac ] While PROBE_MOD_UUID is always part of the base AudioDSP firmware manifest, from maintenance point of view it is better to check the result. Fixes: dab8d000e25c ("ASoC: Intel: avs: Add data probing requests") Signed-off-by: Cezary Rojewski Link: https://msgid.link/r/20240405090929.1184068-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/avs/probes.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 817e543036f2..7e781a315690 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -19,8 +19,11 @@ static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id struct avs_probe_cfg cfg = {{0}}; struct avs_module_entry mentry; u8 dummy; + int ret; - avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + if (ret) + return ret; /* * Probe module uses no cycles, audio data format and input and output @@ -39,11 +42,12 @@ static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id static void avs_dsp_delete_probe(struct avs_dev *adev) { struct avs_module_entry mentry; + int ret; - avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); - - /* There is only ever one probe module instance. */ - avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); + ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + if (!ret) + /* There is only ever one probe module instance. */ + avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); } static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream) -- cgit v1.2.3