mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ASoC: hdac_hda: Fix module unbind/bind via sysfs
Peter Ujfalusi <peter.ujfalusi@linux.intel.com> says: While looking at the reconfig lockup reported in https://bugzilla.kernel.org/show_bug.cgi?id=222182 I checked whether the same can be done by unbinding and binding the HDA codec driver via sysfs: echo -n ehdaudio0D0 > /sys/bus/hdaudio/drivers/snd_hda_codec_alc882/unbind echo -n ehdaudio0D0 > /sys/bus/hdaudio/drivers/snd_hda_codec_alc882/bind The unbind works and the SOF card is removed, but the codec cannot be bound again. This has been broken for a long time with hdac_hda; the reconfig lockup itself is addressed by a separate patch. Patch 1 and 2 fix the bind: the codec private data was lost on unbind and the codec device type was left as HDA_DEV_ASOC, so no HDA codec driver could match it anymore. Patch 2 depends on patch 1, otherwise the bind would dereference NULL drvdata instead of failing. Patch 3 fixes an unbalanced runtime PM disable in the component remove, which leaves the codec with runtime PM disabled after the card is re-created. This is also visible with machine driver unbind/bind. Tested on PTL (ALC888 + iDisp): repeated codec and machine driver unbind/bind, playback/capture, runtime suspend and module reload. Link: https://patch.msgid.link/20261027083840.28813-1-peter.ujfalusi@linux.intel.com
This commit is contained in:
commit
6eba74479d
|
|
@ -554,6 +554,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
|
|||
return;
|
||||
}
|
||||
|
||||
/* prevent runtime PM callbacks while the link and the codec are torn down */
|
||||
pm_runtime_disable(&hdev->dev);
|
||||
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
|
||||
|
||||
|
|
@ -561,6 +562,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
|
|||
driver->ops->remove(codec);
|
||||
|
||||
snd_hda_codec_cleanup_for_unbind(codec);
|
||||
|
||||
/* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */
|
||||
pm_runtime_enable(&hdev->dev);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
|
||||
|
|
@ -617,12 +621,35 @@ static const struct snd_soc_component_driver hdac_hda_hdmi_codec = {
|
|||
.endianness = 1,
|
||||
};
|
||||
|
||||
static void hdac_hda_restore_dev_type(void *data)
|
||||
{
|
||||
struct hdac_device *hdev = data;
|
||||
|
||||
hdev->type = HDA_DEV_LEGACY;
|
||||
}
|
||||
|
||||
static int hdac_hda_dev_probe(struct hdac_device *hdev)
|
||||
{
|
||||
struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev);
|
||||
struct hdac_hda_priv *hda_pvt;
|
||||
struct hdac_ext_link *hlink;
|
||||
int ret;
|
||||
|
||||
hda_pvt = devm_kzalloc(&hdev->dev, sizeof(*hda_pvt), GFP_KERNEL);
|
||||
if (!hda_pvt)
|
||||
return -ENOMEM;
|
||||
|
||||
hda_pvt->codec = dev_to_hda_codec(&hdev->dev);
|
||||
hda_pvt->dev_index = hdev->addr;
|
||||
/* Intel iDisp (HDMI/DP) codec */
|
||||
if (((hdev->vendor_id >> 16) & 0xFFFF) == 0x8086)
|
||||
hda_pvt->need_display_power = true;
|
||||
dev_set_drvdata(&hdev->dev, hda_pvt);
|
||||
|
||||
/* Allow the legacy codec driver to match again after unbind */
|
||||
ret = devm_add_action(&hdev->dev, hdac_hda_restore_dev_type, hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* hold the ref while we probe */
|
||||
hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
|
||||
if (!hlink) {
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
#include "hda.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#include "../../codecs/hdac_hda.h"
|
||||
|
||||
#define CODEC_PROBE_RETRIES 3
|
||||
|
||||
#define IDISP_VID_INTEL 0x80860000
|
||||
|
|
@ -148,7 +146,6 @@ static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, i
|
|||
/* probe individual codec */
|
||||
static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
|
||||
{
|
||||
struct hdac_hda_priv *hda_priv;
|
||||
struct hda_bus *hbus = sof_to_hbus(sdev);
|
||||
struct hda_codec *codec;
|
||||
u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) |
|
||||
|
|
@ -168,27 +165,15 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
|
|||
dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n",
|
||||
address, resp);
|
||||
|
||||
hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL);
|
||||
if (!hda_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY);
|
||||
ret = PTR_ERR_OR_ZERO(codec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hda_priv->codec = codec;
|
||||
hda_priv->dev_index = address;
|
||||
dev_set_drvdata(&codec->core.dev, hda_priv);
|
||||
|
||||
if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) {
|
||||
if (!hbus->core.audio_component) {
|
||||
dev_dbg(sdev->dev,
|
||||
"iDisp hw present but no driver\n");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
hda_priv->need_display_power = true;
|
||||
if ((resp & 0xFFFF0000) == IDISP_VID_INTEL && !hbus->core.audio_component) {
|
||||
dev_dbg(sdev->dev, "iDisp hw present but no driver\n");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_generic_config(hbus))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user