ASoC: hdac: make SOF HDA codec driver probe deterministic
authorKai Vehmanen <kai.vehmanen@linux.intel.com>
Mon, 21 Sep 2020 10:08:41 +0000 (13:08 +0300)
committerMark Brown <broonie@kernel.org>
Mon, 21 Sep 2020 22:57:24 +0000 (23:57 +0100)
To provide backward compatibility to older systems, the SOF HDA driver
allows user to specify which HDMI codec driver to use at runtime via
kernel parameter. This mechanism has a subtle flaw in that it assumes
the codec drivers not to be loaded when the SOF PCI driver is loaded.

The problem is rooted in use of the hdev->type field.
snd_hdac_ext_bus_device_init() initializes this field to HDA_DEV_ASOC.
This signals the HDA core that ASoC drivers should be considered in
driver matching (hda_bus_match()). The SOF and SST drivers continue by
overriding this field to HDA_DEV_LEGACY and proceeding to load driver
modules with request_module(). Correct drivers will get loaded and
attached.

If however the codec drivers are already loaded when
snd_hdac_ext_bus_device_init() is called, the matching will not work as
expected as device type is still set to HDA_DEV_ASOC. Specifically if
hdac-hdmi is attached when machine driver is configured to use hdac-hda,
this leads to out-of-bounds memory access in
hda_dsp_hdmi_build_controls().

Fix the issue by adding codec type as a parameter to
snd_hdac_ext_bus_device_init() and ensuring type is set correctly from
the start.

Fixes: 139c7febad1a ("ASoC: SOF: Intel: add support for snd-hda-codec-hdmi")
Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20200921100841.2882662-1-kai.vehmanen@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
include/sound/hdaudio_ext.h
sound/hda/ext/hdac_ext_bus.c
sound/soc/intel/skylake/skl.c
sound/soc/sof/intel/hda-codec.c

index ef88b20..7abf74c 100644 (file)
@@ -10,7 +10,7 @@ int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
 
 void snd_hdac_ext_bus_exit(struct hdac_bus *bus);
 int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
-                                               struct hdac_device *hdev);
+                               struct hdac_device *hdev, int type);
 void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev);
 void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus);
 
index d0a604c..765c40a 100644 (file)
@@ -70,11 +70,12 @@ static void default_release(struct device *dev)
  * @bus: hdac bus to attach to
  * @addr: codec address
  * @hdev: hdac device to init
+ * @type: codec type (HDAC_DEV_*) to use for this device
  *
  * Returns zero for success or a negative error code.
  */
 int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
-                                       struct hdac_device *hdev)
+                                struct hdac_device *hdev, int type)
 {
        char name[15];
        int ret;
@@ -88,7 +89,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr,
                dev_err(bus->dev, "device init failed for hdac device\n");
                return ret;
        }
-       hdev->type = HDA_DEV_ASOC;
+       hdev->type = type;
        hdev->dev.release = default_release;
 
        ret = snd_hdac_device_register(hdev);
index ea00cf6..8b99372 100644 (file)
@@ -721,7 +721,7 @@ static int probe_codec(struct hdac_bus *bus, int addr)
        hda_codec->codec.bus = skl_to_hbus(skl);
        hdev = &hda_codec->codec.core;
 
-       err = snd_hdac_ext_bus_device_init(bus, addr, hdev);
+       err = snd_hdac_ext_bus_device_init(bus, addr, hdev, HDA_DEV_ASOC);
        if (err < 0)
                return err;
 
@@ -736,7 +736,7 @@ static int probe_codec(struct hdac_bus *bus, int addr)
        if (!hdev)
                return -ENOMEM;
 
-       return snd_hdac_ext_bus_device_init(bus, addr, hdev);
+       return snd_hdac_ext_bus_device_init(bus, addr, hdev, HDA_DEV_ASOC);
 #endif /* CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC */
 }
 
index f6ba3b5..6875fa5 100644 (file)
@@ -117,6 +117,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
        struct hdac_hda_priv *hda_priv;
        struct hda_codec *codec;
+       int type = HDA_DEV_LEGACY;
 #endif
        struct hda_bus *hbus = sof_to_hbus(sdev);
        struct hdac_device *hdev;
@@ -143,7 +144,11 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
        hdev = &hda_priv->codec.core;
        codec = &hda_priv->codec;
 
-       ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev);
+       /* only probe ASoC codec drivers for HDAC-HDMI */
+       if (!hda_codec_use_common_hdmi && (resp & 0xFFFF0000) == IDISP_VID_INTEL)
+               type = HDA_DEV_ASOC;
+
+       ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, type);
        if (ret < 0)
                return ret;
 
@@ -161,13 +166,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
        else
                codec->probe_id = 0;
 
-       /*
-        * if common HDMI codec driver is not used, codec load
-        * is skipped here and hdac_hdmi is used instead
-        */
-       if (hda_codec_use_common_hdmi ||
-           (resp & 0xFFFF0000) != IDISP_VID_INTEL) {
-               hdev->type = HDA_DEV_LEGACY;
+       if (type == HDA_DEV_LEGACY) {
                ret = hda_codec_load_module(codec);
                /*
                 * handle ret==0 (no driver bound) as an error, but pass
@@ -188,7 +187,7 @@ error:
        if (!hdev)
                return -ENOMEM;
 
-       ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev);
+       ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, HDA_DEV_ASOC);
 
        return ret;
 #endif