Commit 27e05fcb authored by Mark Brown's avatar Mark Brown

ASoC: harden SoundWire codec/machine drivers used on Intel platforms

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

While testing fixes for SoundWire race conditions initially reported
in [1], I found additional issues in codec drivers. When the order in
which drivers are probed is changed, multiple errors are reported,
from unbalanced pm_runtime_enable() calls to invalid mutex lock magic
numbers, workqueues not initialized and missing resume sequences.

In 'nominal' usages, there is no change of functionality, this is just
a first step to test random device/driver bind/unbind sequences.

Important note: these changes only touch Intel-based platforms, I
don't have any background and ability to test on Qualcomm-based
devices.

[1] https://lore.kernel.org/alsa-devel/d0559e97-c4a0-b817-428c-d3e305390270@linux.intel.com/

Pierre-Louis Bossart (7):
  ASoC: Realtek/Maxim SoundWire codecs: disable pm_runtime on remove
  ASoC: rt711-sdca-sdw: fix calibrate mutex initialization
  ASoC: Intel: sof_sdw: handle errors on card registration
  ASoC: rt711: fix calibrate mutex initialization
  ASoC: rt7*-sdw: harden jack_detect_handler
  ASoC: codecs: rt700/rt711/rt711-sdca: initialize workqueues in probe
  ASoC: codecs: rt700/rt711/rt711-sdca: resume bus/codec in
    .set_jack_detect

 sound/soc/codecs/max98373-sdw.c   | 12 +++++++-
 sound/soc/codecs/rt1308-sdw.c     | 11 +++++++
 sound/soc/codecs/rt1316-sdw.c     | 11 +++++++
 sound/soc/codecs/rt5682-sdw.c     |  5 ++-
 sound/soc/codecs/rt700-sdw.c      |  6 +++-
 sound/soc/codecs/rt700.c          | 30 +++++++++++-------
 sound/soc/codecs/rt711-sdca-sdw.c |  9 +++++-
 sound/soc/codecs/rt711-sdca.c     | 40 ++++++++++++------------
 sound/soc/codecs/rt711-sdw.c      |  9 +++++-
 sound/soc/codecs/rt711.c          | 40 ++++++++++++------------
 sound/soc/codecs/rt715-sdca-sdw.c | 12 ++++++++
 sound/soc/codecs/rt715-sdw.c      | 12 ++++++++
 sound/soc/intel/boards/sof_sdw.c  | 51 ++++++++++++++++++-------------
 13 files changed, 169 insertions(+), 79 deletions(-)

--
2.34.1
parents 5871321f e02b99e9
......@@ -862,6 +862,16 @@ static int max98373_sdw_probe(struct sdw_slave *slave,
return max98373_init(slave, regmap);
}
static int max98373_sdw_remove(struct sdw_slave *slave)
{
struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
if (max98373->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
#if defined(CONFIG_OF)
static const struct of_device_id max98373_of_match[] = {
{ .compatible = "maxim,max98373", },
......@@ -893,7 +903,7 @@ static struct sdw_driver max98373_sdw_driver = {
.pm = &max98373_pm,
},
.probe = max98373_sdw_probe,
.remove = NULL,
.remove = max98373_sdw_remove,
.ops = &max98373_slave_ops,
.id_table = max98373_id,
};
......
......@@ -691,6 +691,16 @@ static int rt1308_sdw_probe(struct sdw_slave *slave,
return 0;
}
static int rt1308_sdw_remove(struct sdw_slave *slave)
{
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
if (rt1308->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
static const struct sdw_device_id rt1308_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0),
{},
......@@ -750,6 +760,7 @@ static struct sdw_driver rt1308_sdw_driver = {
.pm = &rt1308_pm,
},
.probe = rt1308_sdw_probe,
.remove = rt1308_sdw_remove,
.ops = &rt1308_slave_ops,
.id_table = rt1308_id,
};
......
......@@ -676,6 +676,16 @@ static int rt1316_sdw_probe(struct sdw_slave *slave,
return rt1316_sdw_init(&slave->dev, regmap, slave);
}
static int rt1316_sdw_remove(struct sdw_slave *slave)
{
struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
if (rt1316->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
static const struct sdw_device_id rt1316_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x1316, 0x3, 0x1, 0),
{},
......@@ -735,6 +745,7 @@ static struct sdw_driver rt1316_sdw_driver = {
.pm = &rt1316_pm,
},
.probe = rt1316_sdw_probe,
.remove = rt1316_sdw_remove,
.ops = &rt1316_slave_ops,
.id_table = rt1316_id,
};
......
......@@ -719,9 +719,12 @@ static int rt5682_sdw_remove(struct sdw_slave *slave)
{
struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev);
if (rt5682 && rt5682->hw_init)
if (rt5682->hw_init)
cancel_delayed_work_sync(&rt5682->jack_detect_work);
if (rt5682->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
......
......@@ -13,6 +13,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "rt700.h"
......@@ -463,11 +464,14 @@ static int rt700_sdw_remove(struct sdw_slave *slave)
{
struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev);
if (rt700 && rt700->hw_init) {
if (rt700->hw_init) {
cancel_delayed_work_sync(&rt700->jack_detect_work);
cancel_delayed_work_sync(&rt700->jack_btn_check_work);
}
if (rt700->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
......
......@@ -162,7 +162,7 @@ static void rt700_jack_detect_handler(struct work_struct *work)
if (!rt700->hs_jack)
return;
if (!rt700->component->card->instantiated)
if (!rt700->component->card || !rt700->component->card->instantiated)
return;
reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
......@@ -315,17 +315,27 @@ static int rt700_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack, void *data)
{
struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
int ret;
rt700->hs_jack = hs_jack;
if (!rt700->hw_init) {
dev_dbg(&rt700->slave->dev,
"%s hw_init not ready yet\n", __func__);
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
return ret;
}
/* pm_runtime not enabled yet */
dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
return 0;
}
rt700_jack_init(rt700);
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
}
......@@ -1115,6 +1125,11 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap,
mutex_init(&rt700->disable_irq_lock);
INIT_DELAYED_WORK(&rt700->jack_detect_work,
rt700_jack_detect_handler);
INIT_DELAYED_WORK(&rt700->jack_btn_check_work,
rt700_btn_check_handler);
/*
* Mark hw_init to false
* HW init will be performed when device reports present
......@@ -1209,13 +1224,6 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave)
/* Finish Initial Settings, set power to D3 */
regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
if (!rt700->first_hw_init) {
INIT_DELAYED_WORK(&rt700->jack_detect_work,
rt700_jack_detect_handler);
INIT_DELAYED_WORK(&rt700->jack_btn_check_work,
rt700_btn_check_handler);
}
/*
* if set_jack callback occurred early than io_init,
* we set up the jack detection function now
......
......@@ -11,6 +11,7 @@
#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include "rt711-sdca.h"
#include "rt711-sdca-sdw.h"
......@@ -364,11 +365,17 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave)
{
struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
if (rt711 && rt711->hw_init) {
if (rt711->hw_init) {
cancel_delayed_work_sync(&rt711->jack_detect_work);
cancel_delayed_work_sync(&rt711->jack_btn_check_work);
}
if (rt711->first_hw_init)
pm_runtime_disable(&slave->dev);
mutex_destroy(&rt711->calibrate_mutex);
mutex_destroy(&rt711->disable_irq_lock);
return 0;
}
......
......@@ -294,7 +294,7 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work)
if (!rt711->hs_jack)
return;
if (!rt711->component->card->instantiated)
if (!rt711->component->card || !rt711->component->card->instantiated)
return;
/* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
......@@ -487,16 +487,27 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack, void *data)
{
struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
int ret;
rt711->hs_jack = hs_jack;
if (!rt711->hw_init) {
dev_dbg(&rt711->slave->dev,
"%s hw_init not ready yet\n", __func__);
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
return ret;
}
/* pm_runtime not enabled yet */
dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
return 0;
}
rt711_sdca_jack_init(rt711);
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
}
......@@ -1190,14 +1201,6 @@ static int rt711_sdca_probe(struct snd_soc_component *component)
return 0;
}
static void rt711_sdca_remove(struct snd_soc_component *component)
{
struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt711->regmap, true);
regcache_cache_only(rt711->mbq_regmap, true);
}
static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
.probe = rt711_sdca_probe,
.controls = rt711_sdca_snd_controls,
......@@ -1207,7 +1210,6 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
.dapm_routes = rt711_sdca_audio_map,
.num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
.set_jack = rt711_sdca_set_jack_detect,
.remove = rt711_sdca_remove,
.endianness = 1,
};
......@@ -1412,8 +1414,12 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap,
rt711->regmap = regmap;
rt711->mbq_regmap = mbq_regmap;
mutex_init(&rt711->calibrate_mutex);
mutex_init(&rt711->disable_irq_lock);
INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_sdca_jack_detect_handler);
INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_sdca_btn_check_handler);
/*
* Mark hw_init to false
* HW init will be performed when device reports present
......@@ -1545,14 +1551,6 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
RT711_PUSH_BTN_INT_CTL0, 0x20, 0x00);
if (!rt711->first_hw_init) {
INIT_DELAYED_WORK(&rt711->jack_detect_work,
rt711_sdca_jack_detect_handler);
INIT_DELAYED_WORK(&rt711->jack_btn_check_work,
rt711_sdca_btn_check_handler);
mutex_init(&rt711->calibrate_mutex);
}
/* calibration */
ret = rt711_sdca_calibration(rt711);
if (ret < 0)
......
......@@ -13,6 +13,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "rt711.h"
......@@ -464,12 +465,18 @@ static int rt711_sdw_remove(struct sdw_slave *slave)
{
struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev);
if (rt711 && rt711->hw_init) {
if (rt711->hw_init) {
cancel_delayed_work_sync(&rt711->jack_detect_work);
cancel_delayed_work_sync(&rt711->jack_btn_check_work);
cancel_work_sync(&rt711->calibration_work);
}
if (rt711->first_hw_init)
pm_runtime_disable(&slave->dev);
mutex_destroy(&rt711->calibrate_mutex);
mutex_destroy(&rt711->disable_irq_lock);
return 0;
}
......
......@@ -242,7 +242,7 @@ static void rt711_jack_detect_handler(struct work_struct *work)
if (!rt711->hs_jack)
return;
if (!rt711->component->card->instantiated)
if (!rt711->component->card || !rt711->component->card->instantiated)
return;
if (pm_runtime_status_suspended(rt711->slave->dev.parent)) {
......@@ -457,17 +457,27 @@ static int rt711_set_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *hs_jack, void *data)
{
struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
int ret;
rt711->hs_jack = hs_jack;
if (!rt711->hw_init) {
dev_dbg(&rt711->slave->dev,
"%s hw_init not ready yet\n", __func__);
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
return ret;
}
/* pm_runtime not enabled yet */
dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
return 0;
}
rt711_jack_init(rt711);
pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
}
......@@ -932,13 +942,6 @@ static int rt711_probe(struct snd_soc_component *component)
return 0;
}
static void rt711_remove(struct snd_soc_component *component)
{
struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt711->regmap, true);
}
static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
.probe = rt711_probe,
.set_bias_level = rt711_set_bias_level,
......@@ -949,7 +952,6 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
.dapm_routes = rt711_audio_map,
.num_dapm_routes = ARRAY_SIZE(rt711_audio_map),
.set_jack = rt711_set_jack_detect,
.remove = rt711_remove,
.endianness = 1,
};
......@@ -1204,8 +1206,13 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap,
rt711->sdw_regmap = sdw_regmap;
rt711->regmap = regmap;
mutex_init(&rt711->calibrate_mutex);
mutex_init(&rt711->disable_irq_lock);
INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_jack_detect_handler);
INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_btn_check_handler);
INIT_WORK(&rt711->calibration_work, rt711_calibration_work);
/*
* Mark hw_init to false
* HW init will be performed when device reports present
......@@ -1313,15 +1320,8 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave)
if (rt711->first_hw_init)
rt711_calibration(rt711);
else {
INIT_DELAYED_WORK(&rt711->jack_detect_work,
rt711_jack_detect_handler);
INIT_DELAYED_WORK(&rt711->jack_btn_check_work,
rt711_btn_check_handler);
mutex_init(&rt711->calibrate_mutex);
INIT_WORK(&rt711->calibration_work, rt711_calibration_work);
else
schedule_work(&rt711->calibration_work);
}
/*
* if set_jack callback occurred early than io_init,
......
......@@ -13,6 +13,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "rt715-sdca.h"
......@@ -193,6 +194,16 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave);
}
static int rt715_sdca_sdw_remove(struct sdw_slave *slave)
{
struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
if (rt715->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
static const struct sdw_device_id rt715_sdca_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0),
SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0),
......@@ -267,6 +278,7 @@ static struct sdw_driver rt715_sdw_driver = {
.pm = &rt715_pm,
},
.probe = rt715_sdca_sdw_probe,
.remove = rt715_sdca_sdw_remove,
.ops = &rt715_sdca_slave_ops,
.id_table = rt715_sdca_id,
};
......
......@@ -14,6 +14,7 @@
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <sound/soc.h>
......@@ -514,6 +515,16 @@ static int rt715_sdw_probe(struct sdw_slave *slave,
return 0;
}
static int rt715_sdw_remove(struct sdw_slave *slave)
{
struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
if (rt715->first_hw_init)
pm_runtime_disable(&slave->dev);
return 0;
}
static const struct sdw_device_id rt715_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0),
SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0),
......@@ -575,6 +586,7 @@ static struct sdw_driver rt715_sdw_driver = {
.pm = &rt715_pm,
},
.probe = rt715_sdw_probe,
.remove = rt715_sdw_remove,
.ops = &rt715_slave_ops,
.id_table = rt715_id,
};
......
......@@ -1398,6 +1398,33 @@ static struct snd_soc_card card_sof_sdw = {
.late_probe = sof_sdw_card_late_probe,
};
static void mc_dailink_exit_loop(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
int ret;
int i, j;
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
if (!codec_info_list[i].exit)
continue;
/*
* We don't need to call .exit function if there is no matched
* dai link found.
*/
for_each_card_prelinks(card, j, link) {
if (!strcmp(link->codecs[0].dai_name,
codec_info_list[i].dai_name)) {
ret = codec_info_list[i].exit(card, link);
if (ret)
dev_warn(card->dev,
"codec exit failed %d\n",
ret);
break;
}
}
}
}
static int mc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &card_sof_sdw;
......@@ -1462,6 +1489,7 @@ static int mc_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(card->dev, "snd_soc_register_card failed %d\n", ret);
mc_dailink_exit_loop(card);
return ret;
}
......@@ -1473,29 +1501,8 @@ static int mc_probe(struct platform_device *pdev)
static int mc_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct snd_soc_dai_link *link;
int ret;
int i, j;
for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) {
if (!codec_info_list[i].exit)
continue;
/*
* We don't need to call .exit function if there is no matched
* dai link found.
*/
for_each_card_prelinks(card, j, link) {
if (!strcmp(link->codecs[0].dai_name,
codec_info_list[i].dai_name)) {
ret = codec_info_list[i].exit(card, link);
if (ret)
dev_warn(&pdev->dev,
"codec exit failed %d\n",
ret);
break;
}
}
}
mc_dailink_exit_loop(card);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment