diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c index bc7889c993d05..e33d4360c094f 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650-lpass-lpi.c @@ -220,6 +220,7 @@ static const struct of_device_id lpi_pinctrl_of_match[] = { MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); static const struct dev_pm_ops lpi_pinctrl_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) }; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 8b71513662bb6..3fee3f38a44e8 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1929,10 +1929,12 @@ static int qcom_swrm_setup_lane_provider(struct qcom_swrm_ctrl *ctrl) /* * Hold a link to the provider: this controller dereferences it on every * bank switch and on every DPn access for a borrowed lane, so it must - * not be unbound first. + * not be unbound first. Keep its hclk active while the consumer is, + * and resume it now before probe programs the provider's registers. */ ret = device_link_add(ctrl->dev, &pdev->dev, - DL_FLAG_AUTOREMOVE_CONSUMER) ? 0 : -EINVAL; + DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE) ? 0 : -EINVAL; put_device(&pdev->dev); if (ret) return dev_err_probe(ctrl->dev, ret, @@ -2093,6 +2095,9 @@ static int qcom_swrm_probe(struct platform_device *pdev) * touch the uninitialised sdw_bus. The consumer starts the * frame generator once MM_SYNC ties the two together. */ + /* runtime PM is driven by the consumer through the device link */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); ctrl->lane_provider_ready = true; dev_dbg(dev, "Qualcomm SoundWire lane provider v%x.%x.%x registered\n", @@ -2220,8 +2225,31 @@ static void qcom_swrm_remove(struct platform_device *pdev) { struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev); - if (!ctrl->is_lane_provider) - sdw_bus_master_delete(&ctrl->bus); + if (ctrl->is_lane_provider) { + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + clk_disable_unprepare(ctrl->hclk); + return; + } + + /* delete bus before dropping lane_provider; in-flight ops deref it */ + sdw_bus_master_delete(&ctrl->bus); + + if (ctrl->lane_provider) { + struct qcom_swrm_ctrl *provider = ctrl->lane_provider; + + /* provider hclk may be gated by runtime PM */ + if (pm_runtime_resume_and_get(provider->dev) >= 0) { + provider->reg_write(provider, + provider->reg_layout[SWRM_REG_INTERRUPT_CPU_EN], 0); + /* disable provider SWRM; releases shared SWR_CLK */ + provider->reg_write(provider, SWRM_COMP_CFG_ADDR, 0); + pm_runtime_put(provider->dev); + } + provider->lane_consumer = NULL; + ctrl->lane_provider = NULL; + } + clk_disable_unprepare(ctrl->hclk); } @@ -2230,6 +2258,10 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev) struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); int ret; + /* no bus of its own; the consumer restarts the link */ + if (ctrl->is_lane_provider) + return clk_prepare_enable(ctrl->hclk); + if (ctrl->wake_irq > 0) { if (!irqd_irq_disabled(irq_get_irq_data(ctrl->wake_irq))) disable_irq_nosync(ctrl->wake_irq); @@ -2257,6 +2289,9 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev) reset_control_reset(ctrl->audio_cgcr); qcom_swrm_program_mm_sync(ctrl); + /* provider was resumed first via the device link */ + if (ctrl->lane_provider) + qcom_swrm_program_mm_sync(ctrl->lane_provider); if (ctrl->version == SWRM_VERSION_1_7_0) { ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, ctrl->ee); @@ -2300,6 +2335,12 @@ static int __maybe_unused swrm_runtime_suspend(struct device *dev) struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dev); int ret; + /* the consumer has already stopped the bus clock */ + if (ctrl->is_lane_provider) { + clk_disable_unprepare(ctrl->hclk); + return 0; + } + swrm_wait_for_wr_fifo_done(ctrl); if (!ctrl->clock_stop_not_supported) { /* Mask bus clash interrupt */ @@ -2337,6 +2378,7 @@ static int __maybe_unused swrm_runtime_suspend(struct device *dev) } static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(swrm_runtime_suspend, swrm_runtime_resume, NULL) }; diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index b920868abef14..042bf57bbde20 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3797,7 +3797,6 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx) const char *clk_name = "lpass-rx-mclk"; struct clk_hw *hw; struct clk_init_data init; - int ret; if (rx->npl) parent_clk_name = __clk_get_name(rx->npl); @@ -3811,11 +3810,7 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx) init.num_parents = 1; rx->hw.init = &init; hw = &rx->hw; - ret = devm_clk_hw_register(rx->dev, hw); - if (ret) - return ret; - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); + return devm_clk_hw_register(dev, hw); } static const struct snd_soc_component_driver rx_macro_component_drv = { @@ -3946,6 +3941,15 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) return ret; + /* + * Register before enabling runtime PM so the clk core doesn't wake + * the macro on every clk_summary read. The gate takes runtime PM in + * prepare and is_enabled reads a cached register. + */ + ret = rx_macro_register_mclk_output(rx); + if (ret) + return ret; + pm_runtime_set_autosuspend_delay(dev, 100); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); @@ -3973,7 +3977,7 @@ static int rx_macro_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - ret = rx_macro_register_mclk_output(rx); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &rx->hw); if (ret) goto err_rpm_put; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index b9c44c258e275..add51f771e4c5 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -2685,7 +2685,6 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx) const char *clk_name = "lpass-tx-mclk"; struct clk_hw *hw; struct clk_init_data init; - int ret; if (tx->npl) parent_clk_name = __clk_get_name(tx->npl); @@ -2699,11 +2698,7 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx) init.num_parents = 1; tx->hw.init = &init; hw = &tx->hw; - ret = devm_clk_hw_register(dev, hw); - if (ret) - return ret; - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); + return devm_clk_hw_register(dev, hw); } static const struct snd_soc_component_driver tx_macro_component_drv = { @@ -2801,6 +2796,15 @@ static int tx_macro_probe(struct platform_device *pdev) if (ret) goto err; + /* + * Register before enabling runtime PM so the clk core doesn't wake + * the macro on every clk_summary read. The gate takes runtime PM in + * prepare and is_enabled reads a cached register. + */ + ret = tx_macro_register_mclk_output(tx); + if (ret) + goto err; + pm_runtime_set_autosuspend_delay(dev, 100); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); @@ -2830,7 +2834,7 @@ static int tx_macro_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - ret = tx_macro_register_mclk_output(tx); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &tx->hw); if (ret) goto err_rpm_put; diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 6c9214aa167fa..74f956c736867 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1759,7 +1759,6 @@ static int va_macro_register_fsgen_output(struct va_macro *va) const char *parent_clk_name; const char *clk_name = "fsgen"; struct clk_init_data init; - int ret; if (va->has_npl_clk) parent = va->npl; @@ -1774,11 +1773,7 @@ static int va_macro_register_fsgen_output(struct va_macro *va) init.parent_names = &parent_clk_name; init.num_parents = 1; va->hw.init = &init; - ret = devm_clk_hw_register(va->dev, &va->hw); - if (ret) - return ret; - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &va->hw); + return devm_clk_hw_register(dev, &va->hw); } static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, @@ -1977,6 +1972,15 @@ static int va_macro_probe(struct platform_device *pdev) goto err; } + /* + * Register before enabling runtime PM so the clk core doesn't wake + * the macro on every clk_summary read. The gate takes runtime PM in + * prepare and is_enabled reads a cached register. + */ + ret = va_macro_register_fsgen_output(va); + if (ret) + goto err; + pm_runtime_set_autosuspend_delay(dev, 100); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); @@ -2031,7 +2035,7 @@ static int va_macro_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - ret = va_macro_register_fsgen_output(va); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &va->hw); if (ret) goto err_rpm_put; diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 8e1f502d06e2d..071c337bad3b6 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2691,7 +2691,6 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) const char *parent_clk_name; struct clk_hw *hw; struct clk_init_data init; - int ret; if (wsa->npl) parent_clk_name = __clk_get_name(wsa->npl); @@ -2707,11 +2706,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) init.num_parents = 1; wsa->hw.init = &init; hw = &wsa->hw; - ret = devm_clk_hw_register(wsa->dev, hw); - if (ret) - return ret; - - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); + return devm_clk_hw_register(dev, hw); } static const struct snd_soc_component_driver wsa_macro_component_drv = { @@ -2832,6 +2827,15 @@ static int wsa_macro_probe(struct platform_device *pdev) if (ret) return ret; + /* + * Register before enabling runtime PM so the clk core doesn't wake + * the macro on every clk_summary read. The gate takes runtime PM in + * prepare and is_enabled reads a cached register. + */ + ret = wsa_macro_register_mclk_output(wsa); + if (ret) + return ret; + pm_runtime_set_autosuspend_delay(dev, 100); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); @@ -2859,7 +2863,7 @@ static int wsa_macro_probe(struct platform_device *pdev) if (ret) goto err_rpm_put; - ret = wsa_macro_register_mclk_output(wsa); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &wsa->hw); if (ret) goto err_rpm_put;