Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: intel_auxdevice: add hybrid IDA-based device_number allocation

The IDA-based allocation is useful to simplify debug, but it was also
introduced as a prerequisite to deal with the Intel Lunar Lake
hardware programming sequences: the wake-ups have to be handled with a
system-unique SDI address at the HDaudio controller level.

At the time, the restriction introduced by the IDA to 8 devices total
seemed perfectly fine, but recently hardware vendors created
configurations with more than 8 devices.

Add a new allocation strategy to allow for more than 8 devices using
information on the type of devices, and only use the IDA-based
allocation for devices capable of generating a wake.

In theory the information on wake capabilities should come from
firmware, but none of the existing ACPI tables provide it. The drivers
set the 'wake_capable' property, but this cannot be used reliably: if
the driver probe happens *after* the enumeration, then that property
is not initialized yet. Trying to modify the device_number on-the-fly
proved to be an impossible task generating race conditions left and
right.

The only reliable work-around to control the enumeration is to add a
quirk table. It's ugly but until platform firmware improves, hopefully as a
result of MIPI/SDCA stardization, we can expect that quirk table to
grow for each new headset or microphone codec.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Link: https://lore.kernel.org/r/20230731091333.3593132-4-yung-chuan.liao@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Pierre-Louis Bossart and committed by
Vinod Koul
e66f91a2 39d80b0e

+69 -10
+62 -10
drivers/soundwire/intel_auxdevice.c
··· 23 23 #include "intel.h" 24 24 #include "intel_auxdevice.h" 25 25 26 - /* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */ 27 - #define INTEL_DEV_NUM_IDA_MIN 4 28 - 29 26 #define INTEL_MASTER_SUSPEND_DELAY_MS 3000 30 27 31 28 /* ··· 40 43 static int md_flags; 41 44 module_param_named(sdw_md_flags, md_flags, int, 0444); 42 45 MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); 46 + 47 + struct wake_capable_part { 48 + const u16 mfg_id; 49 + const u16 part_id; 50 + }; 51 + 52 + static struct wake_capable_part wake_capable_list[] = { 53 + {0x025d, 0x5682}, 54 + {0x025d, 0x700}, 55 + {0x025d, 0x711}, 56 + {0x025d, 0x1712}, 57 + {0x025d, 0x1713}, 58 + {0x025d, 0x1716}, 59 + {0x025d, 0x1717}, 60 + {0x025d, 0x712}, 61 + {0x025d, 0x713}, 62 + {0x025d, 0x714}, 63 + {0x025d, 0x715}, 64 + {0x025d, 0x716}, 65 + {0x025d, 0x717}, 66 + {0x025d, 0x722}, 67 + }; 68 + 69 + static bool is_wake_capable(struct sdw_slave *slave) 70 + { 71 + int i; 72 + 73 + for (i = 0; i < ARRAY_SIZE(wake_capable_list); i++) 74 + if (slave->id.part_id == wake_capable_list[i].part_id && 75 + slave->id.mfg_id == wake_capable_list[i].mfg_id) 76 + return true; 77 + return false; 78 + } 43 79 44 80 static int generic_pre_bank_switch(struct sdw_bus *bus) 45 81 { ··· 96 66 { 97 67 struct sdw_cdns *cdns = bus_to_cdns(bus); 98 68 struct sdw_intel *sdw = cdns_to_intel(cdns); 69 + int dev_num_min; 70 + int dev_num_max; 71 + bool wake_capable = slave->prop.wake_capable || is_wake_capable(slave); 72 + 73 + if (wake_capable) { 74 + dev_num_min = SDW_INTEL_DEV_NUM_IDA_MIN; 75 + dev_num_max = SDW_MAX_DEVICES; 76 + } else { 77 + dev_num_min = 1; 78 + dev_num_max = SDW_INTEL_DEV_NUM_IDA_MIN - 1; 79 + } 99 80 100 81 /* paranoia check, this should never happen */ 101 - if (dev_num < INTEL_DEV_NUM_IDA_MIN || dev_num > SDW_MAX_DEVICES) { 102 - dev_err(bus->dev, "%s: invalid dev_num %d\n", __func__, dev_num); 82 + if (dev_num < dev_num_min || dev_num > dev_num_max) { 83 + dev_err(bus->dev, "%s: invalid dev_num %d, wake supported %d\n", 84 + __func__, dev_num, slave->prop.wake_capable); 103 85 return; 104 86 } 105 87 106 - if (sdw->link_res->hw_ops->program_sdi) 88 + if (sdw->link_res->hw_ops->program_sdi && wake_capable) 107 89 sdw->link_res->hw_ops->program_sdi(sdw, dev_num); 108 90 } 109 91 ··· 171 129 172 130 static int intel_get_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave) 173 131 { 174 - return ida_alloc_range(&intel_peripheral_ida, 175 - INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES, 176 - GFP_KERNEL); 132 + int bit; 133 + 134 + if (slave->prop.wake_capable || is_wake_capable(slave)) 135 + return ida_alloc_range(&intel_peripheral_ida, 136 + SDW_INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES, 137 + GFP_KERNEL); 138 + 139 + bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); 140 + if (bit == SDW_MAX_DEVICES) 141 + return -ENODEV; 142 + 143 + return bit; 177 144 } 178 145 179 146 static void intel_put_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave) 180 147 { 181 - return ida_free(&intel_peripheral_ida, slave->dev_num); 148 + if (slave->prop.wake_capable || is_wake_capable(slave)) 149 + ida_free(&intel_peripheral_ida, slave->dev_num); 182 150 } 183 151 184 152 static struct sdw_master_ops sdw_intel_ops = {
+7
include/linux/soundwire/sdw_intel.h
··· 433 433 extern const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops; 434 434 extern const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops; 435 435 436 + /* 437 + * IDA min selected to allow for 5 unconstrained devices per link, 438 + * and 6 system-unique Device Numbers for wake-capable devices. 439 + */ 440 + 441 + #define SDW_INTEL_DEV_NUM_IDA_MIN 6 442 + 436 443 #endif