]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SDCA: Add PDE verification reusable helper
authorNiranjan H Y <niranjan.hy@ti.com>
Tue, 5 May 2026 11:18:03 +0000 (16:48 +0530)
committerMark Brown <broonie@kernel.org>
Thu, 7 May 2026 08:18:37 +0000 (17:18 +0900)
  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>
include/sound/sdca_asoc.h
sound/soc/sdca/sdca_asoc.c

index 46a61a52decc5b95fcd3a2bb25d3d7d8802f9961..ca35d5a44370b9bc00fdd0a735d74da11e6e66cc 100644 (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__
index 2bfc8e5aee31d9f48523a078cca65e6ca134951b..e76afa396b0ab380d6b7dc699176418a5cdfdf5d 100644 (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];
-
-               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;
-       }
+       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);
 
-       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,