soundwire: intel: add probe-time check on link id
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Mon, 19 Aug 2024 00:55:47 +0000 (08:55 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 19 Aug 2024 11:25:52 +0000 (12:25 +0100)
In older platforms, the number of links was constant and hard-coded to
4. Newer platforms can have varying number of links, so we need to add
a probe-time check to make sure the ACPI-reported information with
_DSD properties is aligned with hardware capabilities reported in the
SoundWire LCAP register.

Acked-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Acked-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20240819005548.5867-3-yung-chuan.liao@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/soundwire/intel.h
drivers/soundwire/intel_ace2x.c
drivers/soundwire/intel_auxdevice.c
include/linux/soundwire/sdw_intel.h

index 68838e843b543e4abbb0806849bda8d26ff0500b..1db4d9d3a3ba64b3b547f7de9105c6315ec5d74d 100644 (file)
@@ -222,6 +222,13 @@ static inline bool sdw_intel_sync_check_cmdsync_unlocked(struct sdw_intel *sdw)
        return false;
 }
 
+static inline int sdw_intel_get_link_count(struct sdw_intel *sdw)
+{
+       if (SDW_INTEL_CHECK_OPS(sdw, get_link_count))
+               return SDW_INTEL_OPS(sdw, get_link_count)(sdw);
+       return 4; /* default on older generations */
+}
+
 /* common bus management */
 int intel_start_bus(struct sdw_intel *sdw);
 int intel_start_bus_after_reset(struct sdw_intel *sdw);
index 781fe0aefa68ff1438fa383cb17638305ea58326..fff312c6968dd37c06369b2bd0d49eba0b05f804 100644 (file)
@@ -706,10 +706,30 @@ static void intel_program_sdi(struct sdw_intel *sdw, int dev_num)
                        __func__, sdw->instance, dev_num);
 }
 
+static int intel_get_link_count(struct sdw_intel *sdw)
+{
+       int ret;
+
+       ret = hdac_bus_eml_get_count(sdw->link_res->hbus, true, AZX_REG_ML_LEPTR_ID_SDW);
+       if (!ret) {
+               dev_err(sdw->cdns.dev, "%s: could not retrieve link count\n", __func__);
+               return -ENODEV;
+       }
+
+       if (ret > SDW_INTEL_MAX_LINKS) {
+               dev_err(sdw->cdns.dev, "%s: link count %d exceed max %d\n", __func__, ret, SDW_INTEL_MAX_LINKS);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
 const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops = {
        .debugfs_init = intel_ace2x_debugfs_init,
        .debugfs_exit = intel_ace2x_debugfs_exit,
 
+       .get_link_count = intel_get_link_count,
+
        .register_dai = intel_register_dai,
 
        .check_clock_stop = intel_check_clock_stop,
index 8807e01cbf7c7e530655776f7f4cf2101b588f42..d110f2b587d5e2daec26ddef0471130b278d36b1 100644 (file)
@@ -317,6 +317,20 @@ static int intel_link_probe(struct auxiliary_device *auxdev,
        bus->link_id = auxdev->id;
        bus->clk_stop_timeout = 1;
 
+       /*
+        * paranoia check: make sure ACPI-reported number of links is aligned with
+        * hardware capabilities.
+        */
+       ret = sdw_intel_get_link_count(sdw);
+       if (ret < 0) {
+               dev_err(dev, "%s: sdw_intel_get_link_count failed: %d\n", __func__, ret);
+               return ret;
+       }
+       if (ret <= sdw->instance) {
+               dev_err(dev, "%s: invalid link id %d, link count %d\n", __func__, auxdev->id, ret);
+               return -EINVAL;
+       }
+
        sdw_cdns_probe(cdns);
 
        /* Set ops */
index 87d82ea9a13a76d0537b886c95d44be3d3a1cbf7..cb8e7396b4db1dec6acd318ce6ddfe4d199c102b 100644 (file)
@@ -388,6 +388,7 @@ struct sdw_intel;
 /* struct intel_sdw_hw_ops - SoundWire ops for Intel platforms.
  * @debugfs_init: initialize all debugfs capabilities
  * @debugfs_exit: close and cleanup debugfs capabilities
+ * @get_link_count: fetch link count from hardware registers
  * @register_dai: read all PDI information and register DAIs
  * @check_clock_stop: throw error message if clock is not stopped.
  * @start_bus: normal start
@@ -412,6 +413,8 @@ struct sdw_intel_hw_ops {
        void (*debugfs_init)(struct sdw_intel *sdw);
        void (*debugfs_exit)(struct sdw_intel *sdw);
 
+       int (*get_link_count)(struct sdw_intel *sdw);
+
        int (*register_dai)(struct sdw_intel *sdw);
 
        void (*check_clock_stop)(struct sdw_intel *sdw);