diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c index 2db0c5b30a35..1b28382f6fc0 100644 --- a/asoc/codecs/bolero/bolero-cdc.c +++ b/asoc/codecs/bolero/bolero-cdc.c @@ -14,6 +14,7 @@ #include <soc/snd_event.h> #include <linux/pm_runtime.h> #include <soc/swr-common.h> +#include <dsp/digital-cdc-rsc-mgr.h> #include "bolero-cdc.h" #include "internal.h" #include "bolero-clk-rsc.h" @@ -1379,7 +1380,7 @@ int bolero_runtime_resume(struct device *dev) } if (priv->core_hw_vote_count == 0) { - ret = clk_prepare_enable(priv->lpass_core_hw_vote); + ret = digital_cdc_rsc_mgr_hw_vote_enable(priv->lpass_core_hw_vote); if (ret < 0) { dev_err(dev, "%s:lpass core hw enable failed\n", __func__); @@ -1397,7 +1398,7 @@ int bolero_runtime_resume(struct device *dev) } if (priv->core_audio_vote_count == 0) { - ret = clk_prepare_enable(priv->lpass_audio_hw_vote); + ret = digital_cdc_rsc_mgr_hw_vote_enable(priv->lpass_audio_hw_vote); if (ret < 0) { dev_err(dev, "%s:lpass audio hw enable failed\n", __func__); @@ -1422,7 +1423,8 @@ int bolero_runtime_suspend(struct device *dev) mutex_lock(&priv->vote_lock); if (priv->lpass_core_hw_vote != NULL) { if (--priv->core_hw_vote_count == 0) - clk_disable_unprepare(priv->lpass_core_hw_vote); + digital_cdc_rsc_mgr_hw_vote_disable( + priv->lpass_core_hw_vote); if (priv->core_hw_vote_count < 0) priv->core_hw_vote_count = 0; } else { @@ -1434,7 +1436,8 @@ int bolero_runtime_suspend(struct device *dev) if (priv->lpass_audio_hw_vote != NULL) { if (--priv->core_audio_vote_count == 0) - clk_disable_unprepare(priv->lpass_audio_hw_vote); + digital_cdc_rsc_mgr_hw_vote_disable( + priv->lpass_audio_hw_vote); if (priv->core_audio_vote_count < 0) priv->core_audio_vote_count = 0; } else { diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c index 85d4ae399703..02534d6a4f1f 100644 --- a/asoc/codecs/bolero/va-macro.c +++ b/asoc/codecs/bolero/va-macro.c @@ -16,6 +16,7 @@ #include <asoc/msm-cdc-pinctrl.h> #include <soc/swr-common.h> #include <soc/swr-wcd.h> +#include <dsp/digital-cdc-rsc-mgr.h> #include "bolero-cdc.h" #include "bolero-cdc-registers.h" #include "bolero-clk-rsc.h" @@ -444,7 +445,8 @@ static int va_macro_swr_pwr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: if (va_priv->lpass_audio_hw_vote) { - ret = clk_prepare_enable(va_priv->lpass_audio_hw_vote); + ret = digital_cdc_rsc_mgr_hw_vote_enable( + va_priv->lpass_audio_hw_vote); if (ret) dev_err(va_dev, "%s: lpass audio hw enable failed\n", @@ -467,7 +469,8 @@ static int va_macro_swr_pwr_event(struct snd_soc_dapm_widget *w, if (bolero_tx_clk_switch(component, CLK_SRC_TX_RCG)) dev_dbg(va_dev, "%s: clock switch failed\n",__func__); if (va_priv->lpass_audio_hw_vote) - clk_disable_unprepare(va_priv->lpass_audio_hw_vote); + digital_cdc_rsc_mgr_hw_vote_disable( + va_priv->lpass_audio_hw_vote); break; default: dev_err(va_priv->dev, diff --git a/dsp/Kbuild b/dsp/Kbuild index 006de44589f2..43180e7a3dc3 100644 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -184,6 +184,10 @@ ifdef CONFIG_VOICE_MHI Q6_OBJS += voice_mhi.o endif +ifdef CONFIG_DIGITAL_CDC_RSC_MGR + Q6_OBJS += digital-cdc-rsc-mgr.o +endif + LINUX_INC += -Iinclude/linux INCS += $(COMMON_INC) \ diff --git a/dsp/digital-cdc-rsc-mgr.c b/dsp/digital-cdc-rsc-mgr.c new file mode 100644 index 000000000000..4206523ea85b --- /dev/null +++ b/dsp/digital-cdc-rsc-mgr.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/ratelimit.h> +#include <dsp/digital-cdc-rsc-mgr.h> + +struct mutex hw_vote_lock; +static bool is_init_done; + +/** + * digital_cdc_rsc_mgr_hw_vote_enable - Enables hw vote in DSP + * + * @vote_handle: vote handle for which voting needs to be done + * + * Returns 0 on success or -EINVAL/error code on failure + */ +int digital_cdc_rsc_mgr_hw_vote_enable(struct clk* vote_handle) +{ + int ret = 0; + + if (!is_init_done || vote_handle == NULL) { + pr_err_ratelimited("%s: init failed or vote handle NULL\n", + __func__); + return -EINVAL; + } + + mutex_lock(&hw_vote_lock); + ret = clk_prepare_enable(vote_handle); + mutex_unlock(&hw_vote_lock); + + pr_debug("%s: return %d\n", __func__, ret); + trace_printk("%s: return %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_enable); + +/** + * digital_cdc_rsc_mgr_hw_vote_disable - Disables hw vote in DSP + * + * @vote_handle: vote handle for which voting needs to be disabled + * + */ +void digital_cdc_rsc_mgr_hw_vote_disable(struct clk* vote_handle) +{ + if (!is_init_done || vote_handle == NULL) { + pr_err_ratelimited("%s: init failed or vote handle NULL\n", + __func__); + return; + } + + mutex_lock(&hw_vote_lock); + clk_disable_unprepare(vote_handle); + mutex_unlock(&hw_vote_lock); + trace_printk("%s\n", __func__); +} +EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_disable); + +/** + * digital_cdc_rsc_mgr_hw_vote_reset - Resets hw vote count + * + */ +void digital_cdc_rsc_mgr_hw_vote_reset(struct clk* vote_handle) +{ + int count = 0; + + if (!is_init_done || vote_handle == NULL) { + pr_err_ratelimited("%s: init failed or vote handle NULL\n", + __func__); + return; + } + + mutex_lock(&hw_vote_lock); + while (__clk_is_enabled(vote_handle)) { + clk_disable_unprepare(vote_handle); + count++; + } + pr_debug("%s: Vote count after SSR: %d\n", __func__, count); + trace_printk("%s: Vote count after SSR: %d\n", __func__, count); + + while (count--) + clk_prepare_enable(vote_handle); + mutex_unlock(&hw_vote_lock); +} +EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_reset); + +void digital_cdc_rsc_mgr_init() +{ + mutex_init(&hw_vote_lock); + is_init_done = true; +} + +void digital_cdc_rsc_mgr_exit() +{ + mutex_destroy(&hw_vote_lock); + is_init_done = false; +} diff --git a/dsp/q6_init.c b/dsp/q6_init.c index fb18741d3838..e990f2a15109 100644 --- a/dsp/q6_init.c +++ b/dsp/q6_init.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2019-2020 The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> @@ -24,11 +24,13 @@ static int __init audio_q6_init(void) avtimer_init(); msm_mdf_init(); voice_mhi_init(); + digital_cdc_rsc_mgr_init(); return 0; } static void __exit audio_q6_exit(void) { + digital_cdc_rsc_mgr_exit(); msm_mdf_exit(); avtimer_exit(); audio_slimslave_exit(); diff --git a/dsp/q6_init.h b/dsp/q6_init.h index 4df2e032700f..77ba743484b4 100644 --- a/dsp/q6_init.h +++ b/dsp/q6_init.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #ifndef __Q6_INIT_H__ @@ -80,5 +80,19 @@ static inline void voice_mhi_exit(void) return; } #endif + +#ifdef CONFIG_DIGITAL_CDC_RSC_MGR +void digital_cdc_rsc_mgr_init(void); +void digital_cdc_rsc_mgr_exit(void); +#else +static inline void digital_cdc_rsc_mgr_init(void) +{ +} + +static inline void digital_cdc_rsc_mgr_exit(void) +{ +} +#endif /* CONFIG_DIGITAL_CDC_RSC_MGR */ + #endif diff --git a/include/dsp/digital-cdc-rsc-mgr.h b/include/dsp/digital-cdc-rsc-mgr.h new file mode 100644 index 000000000000..685cd5c1fc5f --- /dev/null +++ b/include/dsp/digital-cdc-rsc-mgr.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef DIGITAL_CDC_RSC_MGR_H +#define DIGITAL_CDC_RSC_MGR_H + +#ifdef CONFIG_DIGITAL_CDC_RSC_MGR + +int digital_cdc_rsc_mgr_hw_vote_enable(struct clk* vote_handle); +void digital_cdc_rsc_mgr_hw_vote_disable(struct clk* vote_handle); +void digital_cdc_rsc_mgr_hw_vote_reset(struct clk* vote_handle); + +#else + +static inline int digital_cdc_rsc_mgr_hw_vote_enable(struct clk* vote_handle) +{ + return 0; +} + +static inline void digital_cdc_rsc_mgr_hw_vote_disable(struct clk* vote_handle) +{ +} + +static inline void digital_cdc_rsc_mgr_hw_vote_reset(struct clk* vote_handle) +{ +} + +#endif /* CONFIG_DIGITAL_CDC_RSC_MGR */ + +#endif /* DIGITAL_CDC_RSC_MGR_H */ diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index c3d2326af593..0b0eef5ab9cc 100644 --- a/soc/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -16,6 +16,7 @@ #include <linux/clk.h> #include <linux/bitops.h> #include <soc/snd_event.h> +#include <dsp/digital-cdc-rsc-mgr.h> #include <linux/pm_runtime.h> #include <dsp/audio_notifier.h> @@ -469,6 +470,7 @@ static int lpi_notifier_service_cb(struct notifier_block *this, unsigned long opcode, void *ptr) { static bool initial_boot = true; + struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev); pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); @@ -484,6 +486,17 @@ static int lpi_notifier_service_cb(struct notifier_block *this, case AUDIO_NOTIFIER_SERVICE_UP: if (initial_boot) initial_boot = false; + + /* Reset HW votes after SSR */ + if (!lpi_dev_up) { + if (state->lpass_core_hw_vote) + digital_cdc_rsc_mgr_hw_vote_reset( + state->lpass_core_hw_vote); + if (state->lpass_audio_hw_vote) + digital_cdc_rsc_mgr_hw_vote_reset( + state->lpass_audio_hw_vote); + } + lpi_dev_up = true; snd_event_notify(lpi_dev, SND_EVENT_UP); break; @@ -870,7 +883,7 @@ int lpi_pinctrl_runtime_resume(struct device *dev) } mutex_lock(&state->core_hw_vote_lock); - ret = clk_prepare_enable(hw_vote); + ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote); if (ret < 0) { pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY_ERROR); @@ -906,7 +919,7 @@ int lpi_pinctrl_runtime_suspend(struct device *dev) mutex_lock(&state->core_hw_vote_lock); if (state->core_hw_vote_status) { - clk_disable_unprepare(hw_vote); + digital_cdc_rsc_mgr_hw_vote_disable(hw_vote); state->core_hw_vote_status = false; } mutex_unlock(&state->core_hw_vote_lock); diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 4aa5c0c6aa5f..77ece45ea049 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -22,6 +22,7 @@ #include <soc/swr-common.h> #include <linux/regmap.h> #include <dsp/msm-audio-event-notify.h> +#include <dsp/digital-cdc-rsc-mgr.h> #include "swrm_registers.h" #include "swr-mstr-ctrl.h" @@ -378,8 +379,8 @@ static int swrm_request_hw_vote(struct swr_mstr_ctrl *swrm, } if (++swrm->hw_core_clk_en == 1) { ret = - clk_prepare_enable( - swrm->lpass_core_hw_vote); + digital_cdc_rsc_mgr_hw_vote_enable( + swrm->lpass_core_hw_vote); if (ret < 0) { dev_err(swrm->dev, "%s:lpass core hw enable failed\n", @@ -392,8 +393,8 @@ static int swrm_request_hw_vote(struct swr_mstr_ctrl *swrm, if (swrm->hw_core_clk_en < 0) swrm->hw_core_clk_en = 0; else if (swrm->hw_core_clk_en == 0) - clk_disable_unprepare( - swrm->lpass_core_hw_vote); + digital_cdc_rsc_mgr_hw_vote_disable( + swrm->lpass_core_hw_vote); } } } @@ -410,8 +411,8 @@ static int swrm_request_hw_vote(struct swr_mstr_ctrl *swrm, } if (++swrm->aud_core_clk_en == 1) { ret = - clk_prepare_enable( - swrm->lpass_core_audio); + digital_cdc_rsc_mgr_hw_vote_enable( + swrm->lpass_core_audio); if (ret < 0) { dev_err(swrm->dev, "%s:lpass audio hw enable failed\n", @@ -424,8 +425,8 @@ static int swrm_request_hw_vote(struct swr_mstr_ctrl *swrm, if (swrm->aud_core_clk_en < 0) swrm->aud_core_clk_en = 0; else if (swrm->aud_core_clk_en == 0) - clk_disable_unprepare( - swrm->lpass_core_audio); + digital_cdc_rsc_mgr_hw_vote_disable( + swrm->lpass_core_audio); } } } @@ -3334,6 +3335,8 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) swrm_device_down(&pdev->dev); mutex_lock(&swrm->devlock); swrm->dev_up = false; + swrm->hw_core_clk_en = 0; + swrm->aud_core_clk_en = 0; mutex_unlock(&swrm->devlock); mutex_lock(&swrm->reslock); swrm->state = SWR_MSTR_SSR;