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

ASoC: SDCA: Add completion for FDL start and stop

Add some completions and a helper function to allow other parts of the
system to wait for FDL to complete. The sdca_fdl_sync() function will
wait until it completes a full time out without a new FDL request
happening, this ensures that even parts requiring multiple rounds of FDL
should be fully downloaded before the driver boot continues.

Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://patch.msgid.link/20251020155512.353774-17-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Charles Keepax and committed by
Mark Brown
0723affa aeaf27ec

+85
+10
include/sound/sdca_fdl.h
··· 10 10 #ifndef __SDCA_FDL_H__ 11 11 #define __SDCA_FDL_H__ 12 12 13 + #include <linux/completion.h> 14 + 13 15 struct device; 14 16 struct regmap; 15 17 struct sdca_fdl_set; 16 18 struct sdca_function_data; 17 19 struct sdca_interrupt; 20 + struct sdca_interrupt_info; 18 21 19 22 /** 20 23 * struct fdl_state - FDL state structure to keep data between interrupts 24 + * @begin: Completion indicating the start of an FDL download cycle. 25 + * @done: Completion indicating the end of an FDL download cycle. 21 26 * @set: Pointer to the FDL set currently being downloaded. 22 27 * @file_index: Index of the current file being processed. 23 28 */ 24 29 struct fdl_state { 30 + struct completion begin; 31 + struct completion done; 32 + 25 33 struct sdca_fdl_set *set; 26 34 int file_index; 27 35 }; ··· 59 51 60 52 int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt); 61 53 int sdca_fdl_process(struct sdca_interrupt *interrupt); 54 + int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, 55 + struct sdca_interrupt_info *info); 62 56 63 57 int sdca_reset_function(struct device *dev, struct sdca_function_data *function, 64 58 struct regmap *regmap);
+75
sound/soc/sdca/sdca_fdl.c
··· 14 14 #include <linux/firmware.h> 15 15 #include <linux/module.h> 16 16 #include <linux/pci.h> 17 + #include <linux/pm_runtime.h> 17 18 #include <linux/regmap.h> 18 19 #include <linux/sprintf.h> 19 20 #include <linux/soundwire/sdw.h> ··· 63 62 return 0; 64 63 } 65 64 EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA"); 65 + 66 + /** 67 + * sdca_fdl_sync - wait for a function to finish FDL 68 + * @dev: Device pointer for error messages. 69 + * @function: Pointer to the SDCA Function. 70 + * @info: Pointer to the SDCA interrupt info for this device. 71 + * 72 + * Return: Zero on success or a negative error code. 73 + */ 74 + int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, 75 + struct sdca_interrupt_info *info) 76 + { 77 + static const int fdl_retries = 6; 78 + unsigned long begin_timeout = msecs_to_jiffies(100); 79 + unsigned long done_timeout = msecs_to_jiffies(4000); 80 + int nfdl; 81 + int i, j; 82 + 83 + for (i = 0; i < fdl_retries; i++) { 84 + nfdl = 0; 85 + 86 + for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) { 87 + struct sdca_interrupt *interrupt = &info->irqs[j]; 88 + struct fdl_state *fdl_state; 89 + unsigned long time; 90 + 91 + if (interrupt->function != function || 92 + !interrupt->entity || !interrupt->control || 93 + interrupt->entity->type != SDCA_ENTITY_TYPE_XU || 94 + interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) 95 + continue; 96 + 97 + fdl_state = interrupt->priv; 98 + nfdl++; 99 + 100 + /* 101 + * Looking for timeout without any new FDL requests 102 + * to imply the device has completed initial 103 + * firmware setup. Alas the specification doesn't 104 + * have any mechanism to detect this. 105 + */ 106 + time = wait_for_completion_timeout(&fdl_state->begin, 107 + begin_timeout); 108 + if (!time) { 109 + dev_dbg(dev, "no new FDL starts\n"); 110 + nfdl--; 111 + continue; 112 + } 113 + 114 + time = wait_for_completion_timeout(&fdl_state->done, 115 + done_timeout); 116 + if (!time) { 117 + dev_err(dev, "timed out waiting for FDL to complete\n"); 118 + return -ETIMEDOUT; 119 + } 120 + } 121 + 122 + if (!nfdl) 123 + return 0; 124 + } 125 + 126 + dev_err(dev, "too many FDL requests\n"); 127 + return -ETIMEDOUT; 128 + } 129 + EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA"); 66 130 67 131 static char *fdl_get_sku_filename(struct device *dev, 68 132 struct sdca_fdl_file *fdl_file) ··· 296 230 297 231 fdl_state->set = NULL; 298 232 233 + pm_runtime_put(interrupt->dev); 234 + complete(&fdl_state->done); 235 + 299 236 dev_dbg(interrupt->dev, "completed FDL process\n"); 300 237 } 301 238 ··· 310 241 switch (status) { 311 242 case SDCA_CTL_XU_FDLD_NEEDS_SET: 312 243 dev_dbg(interrupt->dev, "starting FDL process...\n"); 244 + 245 + pm_runtime_get(interrupt->dev); 246 + complete(&fdl_state->begin); 313 247 314 248 fdl_state->file_index = 0; 315 249 fdl_state->set = fdl_get_set(interrupt); ··· 440 368 fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL); 441 369 if (!fdl_state) 442 370 return -ENOMEM; 371 + 372 + init_completion(&fdl_state->begin); 373 + init_completion(&fdl_state->done); 443 374 444 375 interrupt->priv = fdl_state; 445 376