mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 13:30:45 -05:00
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>
This commit is contained in:
committed by
Mark Brown
parent
aeaf27ec65
commit
0723affa1b
@@ -10,18 +10,26 @@
|
||||
#ifndef __SDCA_FDL_H__
|
||||
#define __SDCA_FDL_H__
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
||||
struct device;
|
||||
struct regmap;
|
||||
struct sdca_fdl_set;
|
||||
struct sdca_function_data;
|
||||
struct sdca_interrupt;
|
||||
struct sdca_interrupt_info;
|
||||
|
||||
/**
|
||||
* struct fdl_state - FDL state structure to keep data between interrupts
|
||||
* @begin: Completion indicating the start of an FDL download cycle.
|
||||
* @done: Completion indicating the end of an FDL download cycle.
|
||||
* @set: Pointer to the FDL set currently being downloaded.
|
||||
* @file_index: Index of the current file being processed.
|
||||
*/
|
||||
struct fdl_state {
|
||||
struct completion begin;
|
||||
struct completion done;
|
||||
|
||||
struct sdca_fdl_set *set;
|
||||
int file_index;
|
||||
};
|
||||
@@ -51,6 +59,8 @@ struct fdl_state {
|
||||
|
||||
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt);
|
||||
int sdca_fdl_process(struct sdca_interrupt *interrupt);
|
||||
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
|
||||
struct sdca_interrupt_info *info);
|
||||
|
||||
int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
|
||||
struct regmap *regmap);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
@@ -63,6 +64,71 @@ int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
|
||||
}
|
||||
EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA");
|
||||
|
||||
/**
|
||||
* sdca_fdl_sync - wait for a function to finish FDL
|
||||
* @dev: Device pointer for error messages.
|
||||
* @function: Pointer to the SDCA Function.
|
||||
* @info: Pointer to the SDCA interrupt info for this device.
|
||||
*
|
||||
* Return: Zero on success or a negative error code.
|
||||
*/
|
||||
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
|
||||
struct sdca_interrupt_info *info)
|
||||
{
|
||||
static const int fdl_retries = 6;
|
||||
unsigned long begin_timeout = msecs_to_jiffies(100);
|
||||
unsigned long done_timeout = msecs_to_jiffies(4000);
|
||||
int nfdl;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < fdl_retries; i++) {
|
||||
nfdl = 0;
|
||||
|
||||
for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) {
|
||||
struct sdca_interrupt *interrupt = &info->irqs[j];
|
||||
struct fdl_state *fdl_state;
|
||||
unsigned long time;
|
||||
|
||||
if (interrupt->function != function ||
|
||||
!interrupt->entity || !interrupt->control ||
|
||||
interrupt->entity->type != SDCA_ENTITY_TYPE_XU ||
|
||||
interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
|
||||
continue;
|
||||
|
||||
fdl_state = interrupt->priv;
|
||||
nfdl++;
|
||||
|
||||
/*
|
||||
* Looking for timeout without any new FDL requests
|
||||
* to imply the device has completed initial
|
||||
* firmware setup. Alas the specification doesn't
|
||||
* have any mechanism to detect this.
|
||||
*/
|
||||
time = wait_for_completion_timeout(&fdl_state->begin,
|
||||
begin_timeout);
|
||||
if (!time) {
|
||||
dev_dbg(dev, "no new FDL starts\n");
|
||||
nfdl--;
|
||||
continue;
|
||||
}
|
||||
|
||||
time = wait_for_completion_timeout(&fdl_state->done,
|
||||
done_timeout);
|
||||
if (!time) {
|
||||
dev_err(dev, "timed out waiting for FDL to complete\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nfdl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(dev, "too many FDL requests\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA");
|
||||
|
||||
static char *fdl_get_sku_filename(struct device *dev,
|
||||
struct sdca_fdl_file *fdl_file)
|
||||
{
|
||||
@@ -230,6 +296,9 @@ static void fdl_end(struct sdca_interrupt *interrupt)
|
||||
|
||||
fdl_state->set = NULL;
|
||||
|
||||
pm_runtime_put(interrupt->dev);
|
||||
complete(&fdl_state->done);
|
||||
|
||||
dev_dbg(interrupt->dev, "completed FDL process\n");
|
||||
}
|
||||
|
||||
@@ -242,6 +311,9 @@ static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int sta
|
||||
case SDCA_CTL_XU_FDLD_NEEDS_SET:
|
||||
dev_dbg(interrupt->dev, "starting FDL process...\n");
|
||||
|
||||
pm_runtime_get(interrupt->dev);
|
||||
complete(&fdl_state->begin);
|
||||
|
||||
fdl_state->file_index = 0;
|
||||
fdl_state->set = fdl_get_set(interrupt);
|
||||
fallthrough;
|
||||
@@ -369,6 +441,9 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
|
||||
if (!fdl_state)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&fdl_state->begin);
|
||||
init_completion(&fdl_state->done);
|
||||
|
||||
interrupt->priv = fdl_state;
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user