[RFC PATCH 06/23] ASoC: codecs: wsa884x: register the die temperature as a thermal zone

From: Srinivas Kandagatla

Date: Fri Oct 23 2026 - 13:22:31 EST


Speaker protection pairs each speaker's calibrated resistance with the
amplifier die temperature (T0) at the time, but an in-kernel user has
no way to read that temperature.

Register a tripless thermal zone over the existing temperature read,
named "wsa-<bus id>-<unique id>" so a user holding the codec's device
can find it with thermal_zone_get_zone_by_name(). Until the codec is
initialised, and while the PA is on with nothing cached yet, return
-EAGAIN to hwmon and no valid temperature to the thermal zone, instead
of a plausible looking zero. -EAGAIN would make the thermal core re-read
the zone every 250 ms and keep the codec awake.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/wsa884x.c | 70 +++++++++++++++++++++++++++++++++++++-
1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index fbb9c5773aa5..ecc6983bb54e 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -23,6 +23,7 @@
#include <linux/soundwire/sdw_type.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/thermal.h>
#include <sound/soc-dapm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
@@ -1928,8 +1929,10 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
if (wsa884x->pa_on) {
/*
* Reading temperature is possible only when Power Amplifier is
- * off. Report last cached data.
+ * off. Report last cached data if we have one, -EAGAIN otherwise.
*/
+ if (!wsa884x->temperature)
+ return -EAGAIN;
*temp = wsa884x->temperature * 1000;
return 0;
}
@@ -2051,6 +2054,42 @@ static const struct hwmon_chip_info wsa884x_hwmon_chip_info = {
.info = wsa884x_hwmon_info,
};

+static int wsa884x_tz_get_temp(struct thermal_zone_device *tz, int *temp_mdeg)
+{
+ struct wsa884x_priv *wsa884x = thermal_zone_device_priv(tz);
+ long mdeg;
+ int ret;
+
+ /*
+ * OTP trims read back as zero until the codec is initialised. Report
+ * no reading rather than -EAGAIN, which makes the core poll the zone.
+ */
+ if (!wsa884x->hw_init) {
+ *temp_mdeg = THERMAL_TEMP_INVALID;
+ return 0;
+ }
+
+ ret = wsa884x_get_temp(wsa884x, &mdeg);
+ if (ret == -EAGAIN) {
+ *temp_mdeg = THERMAL_TEMP_INVALID;
+ return 0;
+ }
+ if (ret)
+ return ret;
+
+ *temp_mdeg = (int)mdeg;
+ return 0;
+}
+
+static const struct thermal_zone_device_ops wsa884x_tz_ops = {
+ .get_temp = wsa884x_tz_get_temp,
+};
+
+static void wsa884x_tz_unregister(void *data)
+{
+ thermal_zone_device_unregister(data);
+}
+
static void wsa884x_reset_powerdown(void *data)
{
struct wsa884x_priv *wsa884x = data;
@@ -2187,6 +2226,35 @@ static int wsa884x_probe(struct sdw_slave *pdev,
if (ret)
return ret;

+ if (IS_REACHABLE(CONFIG_THERMAL)) {
+ char tz_name[THERMAL_NAME_LENGTH];
+ struct thermal_zone_device *tz;
+
+ /*
+ * Register after runtime PM is enabled, as the zone is read
+ * right away. dev_name() is too long for a zone name.
+ */
+ scnprintf(tz_name, sizeof(tz_name), "wsa-%d-%u",
+ pdev->bus->id, pdev->id.unique_id);
+ tz = thermal_tripless_zone_device_register(tz_name,
+ wsa884x,
+ &wsa884x_tz_ops,
+ NULL);
+ if (IS_ERR(tz)) {
+ dev_warn(dev,
+ "thermal zone '%s' registration failed (%ld); SP T0 kcontrol will read 0 for this codec\n",
+ tz_name, PTR_ERR(tz));
+ } else {
+ ret = devm_add_action_or_reset(dev,
+ wsa884x_tz_unregister,
+ tz);
+ if (ret)
+ return ret;
+
+ thermal_zone_device_enable(tz);
+ }
+ }
+
return devm_snd_soc_register_component(dev,
&wsa884x_component_drv,
wsa884x_dais,
--
2.47.3