ASoC: SDCA: Add PDE verification reusable helper

Implement sdca_asoc_pde_ensure_ps() helper function to poll for PDE
power state transitions. Per SDCA specification, after writing
REQUESTED_PS, drivers must poll ACTUAL_PS until the target power state
is reached.

Changes include:
- Add sdca_asoc_pde_ensure_ps() to handle ACTUAL_PS polling with
  support for device-specific delay tables or default intervals
- Export function via sdca_asoc.h for use by SDCA-compliant drivers
- Refactor entity_pde_event() in sdca_asoc.c to use the helper

Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://patch.msgid.link/20260505111806.2280-2-niranjan.hy@ti.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Niranjan H Y
2026-05-05 16:48:03 +05:30
committed by Mark Brown
parent 36645381b8
commit cdff2e6ab8
2 changed files with 82 additions and 30 deletions

View File

@@ -13,6 +13,7 @@
struct device;
struct regmap;
struct sdca_function_data;
struct sdca_pde_delay;
struct snd_ctl_elem_value;
struct snd_kcontrol;
struct snd_kcontrol_new;
@@ -99,4 +100,9 @@ int sdca_asoc_q78_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int sdca_asoc_q78_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap,
int function_id, int entity_id,
int from_ps, int to_ps,
const struct sdca_pde_delay *pde_delays,
int num_delays);
#endif // __SDCA_ASOC_H__

View File

@@ -359,15 +359,77 @@ static int entity_parse_ot(struct device *dev,
return 0;
}
/**
* sdca_asoc_pde_poll_actual_ps - Verify PDE power state reached target state
* @dev: Pointer to the device for error logging.
* @regmap: Register map for reading ACTUAL_PS register.
* @function_id: SDCA function identifier.
* @entity_id: SDCA entity identifier for the power domain.
* @from_ps: Source power state (SDCA_PDE_PSn value).
* @to_ps: Target power state (SDCA_PDE_PSn value).
* @pde_delays: Pointer to array of PDE delay specifications for this device,
* or NULL to use default polling interval.
* @num_delays: Number of entries in pde_delays array.
*
* This function polls the ACTUAL_PS register to verify that a PDE power state
* transition has completed. Per SDCA specification, after writing REQUESTED_PS,
* the caller must poll ACTUAL_PS until it reflects the requested state.
*
* This function implements the polling logic but does NOT modify the power state.
* The caller is responsible for writing REQUESTED_PS before invoking this function.
*
* If a delay table is provided, appropriate polling intervals are extracted based
* on the from_ps and to_ps transition. If no table is provided or no matching entry
* is found, a default polling interval is used.
*
* Return: Returns zero when ACTUAL_PS reaches the target state, -ETIMEDOUT if the
* polling times out before reaching the target state, or a negative error code if
* a register read fails.
*/
int sdca_asoc_pde_poll_actual_ps(struct device *dev, struct regmap *regmap,
int function_id, int entity_id,
int from_ps, int to_ps,
const struct sdca_pde_delay *pde_delays,
int num_delays)
{
static const int polls = 100;
static const int default_poll_us = 1000;
unsigned int reg, val;
int i, poll_us = default_poll_us;
int ret;
if (pde_delays && num_delays > 0) {
for (i = 0; i < num_delays; i++) {
if (pde_delays[i].from_ps == from_ps && pde_delays[i].to_ps == to_ps) {
poll_us = pde_delays[i].us / polls;
break;
}
}
}
reg = SDW_SDCA_CTL(function_id, entity_id, SDCA_CTL_PDE_ACTUAL_PS, 0);
for (i = 0; i < polls; i++) {
if (i)
fsleep(poll_us);
ret = regmap_read(regmap, reg, &val);
if (ret)
return ret;
else if (val == to_ps)
return 0;
}
return -ETIMEDOUT;
}
EXPORT_SYMBOL_NS(sdca_asoc_pde_poll_actual_ps, "SND_SOC_SDCA");
static int entity_pde_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kctl, int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
struct sdca_entity *entity = widget->priv;
static const int polls = 100;
unsigned int reg, val;
int from, to, i;
int poll_us;
int from, to;
int ret;
if (!component)
@@ -386,33 +448,17 @@ static int entity_pde_event(struct snd_soc_dapm_widget *widget,
return 0;
}
for (i = 0; i < entity->pde.num_max_delay; i++) {
struct sdca_pde_delay *delay = &entity->pde.max_delay[i];
ret = sdca_asoc_pde_poll_actual_ps(component->dev, component->regmap,
SDW_SDCA_CTL_FUNC(widget->reg),
SDW_SDCA_CTL_ENT(widget->reg),
from, to,
entity->pde.max_delay,
entity->pde.num_max_delay);
if (ret)
dev_err(component->dev, "%s: PDE transition %x -> %x failed, err=%d\n",
entity->label, from, to, ret);
if (delay->from_ps == from && delay->to_ps == to) {
poll_us = delay->us / polls;
break;
}
}
reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(widget->reg),
SDW_SDCA_CTL_ENT(widget->reg),
SDCA_CTL_PDE_ACTUAL_PS, 0);
for (i = 0; i < polls; i++) {
if (i)
fsleep(poll_us);
ret = regmap_read(component->regmap, reg, &val);
if (ret)
return ret;
else if (val == to)
return 0;
}
dev_err(component->dev, "%s: power transition failed: %x\n",
entity->label, val);
return -ETIMEDOUT;
return ret;
}
static int entity_parse_pde(struct device *dev,