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

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: codecs: wsa883x: Implement temperature reading and hwmon

Read temperature of the amplifier and expose it via hwmon interface, which
will be later used during calibration of speaker protection algorithms.
The method is the same as for wsa884x and therefore this is based on
Krzysztof Kozlowski's approach implemented in commit 6b99dc62d940 ("ASoC:
codecs: wsa884x: Implement temperature reading and hwmon").

Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Steev Klimaszewski <steev@kali.org>
Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
Tested-by: Steev Klimaszewski <steev@kali.org> #Thinkpad X13s
Link: https://patch.msgid.link/20250221032141.1206902-1-alexey.klimov@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Alexey Klimov and committed by
Mark Brown
a02c42d4 feb84940

+194
+194
sound/soc/codecs/wsa883x.c
··· 6 6 #include <linux/bitops.h> 7 7 #include <linux/device.h> 8 8 #include <linux/gpio/consumer.h> 9 + #include <linux/hwmon.h> 9 10 #include <linux/init.h> 10 11 #include <linux/kernel.h> 11 12 #include <linux/module.h> ··· 157 156 #define WSA883X_PA_FSM_ERR_COND (WSA883X_DIG_CTRL_BASE + 0x0014) 158 157 #define WSA883X_PA_FSM_MSK (WSA883X_DIG_CTRL_BASE + 0x0015) 159 158 #define WSA883X_PA_FSM_BYP (WSA883X_DIG_CTRL_BASE + 0x0016) 159 + #define WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK 0x01 160 + #define WSA883X_PA_FSM_BYP_DC_CAL_EN_SHIFT 0 161 + #define WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK 0x02 162 + #define WSA883X_PA_FSM_BYP_CLK_WD_EN_SHIFT 1 163 + #define WSA883X_PA_FSM_BYP_BG_EN_MASK 0x04 164 + #define WSA883X_PA_FSM_BYP_BG_EN_SHIFT 2 165 + #define WSA883X_PA_FSM_BYP_BOOST_EN_MASK 0x08 166 + #define WSA883X_PA_FSM_BYP_BOOST_EN_SHIFT 3 167 + #define WSA883X_PA_FSM_BYP_PA_EN_MASK 0x10 168 + #define WSA883X_PA_FSM_BYP_PA_EN_SHIFT 4 169 + #define WSA883X_PA_FSM_BYP_D_UNMUTE_MASK 0x20 170 + #define WSA883X_PA_FSM_BYP_D_UNMUTE_SHIFT 5 171 + #define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK 0x40 172 + #define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_SHIFT 6 173 + #define WSA883X_PA_FSM_BYP_TSADC_EN_MASK 0x80 174 + #define WSA883X_PA_FSM_BYP_TSADC_EN_SHIFT 7 160 175 #define WSA883X_PA_FSM_DBG (WSA883X_DIG_CTRL_BASE + 0x0017) 161 176 #define WSA883X_TADC_VALUE_CTL (WSA883X_DIG_CTRL_BASE + 0x0020) 177 + #define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01 178 + #define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0 179 + #define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02 180 + #define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1 162 181 #define WSA883X_TEMP_DETECT_CTL (WSA883X_DIG_CTRL_BASE + 0x0021) 163 182 #define WSA883X_TEMP_MSB (WSA883X_DIG_CTRL_BASE + 0x0022) 164 183 #define WSA883X_TEMP_LSB (WSA883X_DIG_CTRL_BASE + 0x0023) ··· 448 427 SNDRV_PCM_FMTBIT_S24_LE |\ 449 428 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) 450 429 430 + /* Two-point trimming for temperature calibration */ 431 + #define WSA883X_T1_TEMP -10L 432 + #define WSA883X_T2_TEMP 150L 433 + 434 + /* 435 + * Device will report senseless data in many cases, so discard any measurements 436 + * outside of valid range. 437 + */ 438 + #define WSA883X_LOW_TEMP_THRESHOLD 5 439 + #define WSA883X_HIGH_TEMP_THRESHOLD 45 440 + 451 441 struct wsa883x_priv { 452 442 struct regmap *regmap; 453 443 struct device *dev; ··· 473 441 int active_ports; 474 442 int dev_mode; 475 443 int comp_offset; 444 + /* 445 + * Protects temperature reading code (related to speaker protection) and 446 + * fields: temperature and pa_on. 447 + */ 448 + struct mutex sp_lock; 449 + unsigned int temperature; 450 + bool pa_on; 476 451 }; 477 452 478 453 enum { ··· 1225 1186 1226 1187 switch (event) { 1227 1188 case SND_SOC_DAPM_POST_PMU: 1189 + mutex_lock(&wsa883x->sp_lock); 1190 + wsa883x->pa_on = true; 1191 + mutex_unlock(&wsa883x->sp_lock); 1192 + 1228 1193 switch (wsa883x->dev_mode) { 1229 1194 case RECEIVER: 1230 1195 snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE, ··· 1278 1235 WSA883X_GLOBAL_PA_EN_MASK, 0); 1279 1236 snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, 1280 1237 WSA883X_PDM_EN_MASK, 0); 1238 + mutex_lock(&wsa883x->sp_lock); 1239 + wsa883x->pa_on = false; 1240 + mutex_unlock(&wsa883x->sp_lock); 1281 1241 break; 1282 1242 } 1283 1243 return 0; ··· 1413 1367 }, 1414 1368 }; 1415 1369 1370 + static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) 1371 + { 1372 + unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0; 1373 + unsigned int dmeas_msb = 0, dmeas_lsb = 0; 1374 + int d1, d2, dmeas; 1375 + unsigned int mask; 1376 + int ret, range; 1377 + long val; 1378 + 1379 + guard(mutex)(&wsa883x->sp_lock); 1380 + 1381 + if (wsa883x->pa_on) { 1382 + /* 1383 + * Reading temperature is possible only when Power Amplifier is 1384 + * off. Report last cached data. 1385 + */ 1386 + *temp = wsa883x->temperature * 1000; 1387 + return 0; 1388 + } 1389 + 1390 + ret = pm_runtime_resume_and_get(wsa883x->dev); 1391 + if (ret < 0) 1392 + return ret; 1393 + 1394 + mask = WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK | 1395 + WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK | 1396 + WSA883X_PA_FSM_BYP_BG_EN_MASK | 1397 + WSA883X_PA_FSM_BYP_D_UNMUTE_MASK | 1398 + WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK | 1399 + WSA883X_PA_FSM_BYP_TSADC_EN_MASK; 1400 + 1401 + /* 1402 + * Here and further do not care about read or update failures. 1403 + * For example, before turning the amplifier on for the first 1404 + * time, reading WSA883X_TEMP_DIN_MSB will always return 0. 1405 + * Instead, check if returned value is within reasonable 1406 + * thresholds. 1407 + */ 1408 + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, mask); 1409 + 1410 + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, 1411 + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 1412 + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0)); 1413 + 1414 + regmap_read(wsa883x->regmap, WSA883X_TEMP_MSB, &dmeas_msb); 1415 + regmap_read(wsa883x->regmap, WSA883X_TEMP_LSB, &dmeas_lsb); 1416 + 1417 + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, 1418 + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 1419 + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1)); 1420 + 1421 + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_1, &d1_msb); 1422 + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_2, &d1_lsb); 1423 + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_3, &d2_msb); 1424 + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_4, &d2_lsb); 1425 + 1426 + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, 0x0); 1427 + 1428 + dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6; 1429 + d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6; 1430 + d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6; 1431 + 1432 + if (d1 == d2) { 1433 + /* Incorrect data in OTP? */ 1434 + ret = -EINVAL; 1435 + goto out; 1436 + } 1437 + 1438 + val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1)); 1439 + range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD; 1440 + if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) { 1441 + wsa883x->temperature = val; 1442 + *temp = val * 1000; 1443 + ret = 0; 1444 + } else { 1445 + ret = -EAGAIN; 1446 + } 1447 + out: 1448 + pm_runtime_mark_last_busy(wsa883x->dev); 1449 + pm_runtime_put_autosuspend(wsa883x->dev); 1450 + 1451 + return ret; 1452 + } 1453 + 1454 + static umode_t wsa883x_hwmon_is_visible(const void *data, 1455 + enum hwmon_sensor_types type, u32 attr, 1456 + int channel) 1457 + { 1458 + if (type != hwmon_temp) 1459 + return 0; 1460 + 1461 + switch (attr) { 1462 + case hwmon_temp_input: 1463 + return 0444; 1464 + default: 1465 + break; 1466 + } 1467 + 1468 + return 0; 1469 + } 1470 + 1471 + static int wsa883x_hwmon_read(struct device *dev, 1472 + enum hwmon_sensor_types type, 1473 + u32 attr, int channel, long *temp) 1474 + { 1475 + int ret; 1476 + 1477 + switch (attr) { 1478 + case hwmon_temp_input: 1479 + ret = wsa883x_get_temp(dev_get_drvdata(dev), temp); 1480 + break; 1481 + default: 1482 + ret = -EOPNOTSUPP; 1483 + break; 1484 + } 1485 + 1486 + return ret; 1487 + } 1488 + 1489 + static const struct hwmon_channel_info *const wsa883x_hwmon_info[] = { 1490 + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 1491 + NULL 1492 + }; 1493 + 1494 + static const struct hwmon_ops wsa883x_hwmon_ops = { 1495 + .is_visible = wsa883x_hwmon_is_visible, 1496 + .read = wsa883x_hwmon_read, 1497 + }; 1498 + 1499 + static const struct hwmon_chip_info wsa883x_hwmon_chip_info = { 1500 + .ops = &wsa883x_hwmon_ops, 1501 + .info = wsa883x_hwmon_info, 1502 + }; 1503 + 1416 1504 static int wsa883x_probe(struct sdw_slave *pdev, 1417 1505 const struct sdw_device_id *id) 1418 1506 { ··· 1582 1402 wsa883x->sconfig.bps = 1; 1583 1403 wsa883x->sconfig.direction = SDW_DATA_DIR_RX; 1584 1404 wsa883x->sconfig.type = SDW_STREAM_PDM; 1405 + mutex_init(&wsa883x->sp_lock); 1585 1406 1586 1407 /** 1587 1408 * Port map index starts with 0, however the data port for this codec ··· 1605 1424 "regmap_init failed\n"); 1606 1425 goto err; 1607 1426 } 1427 + 1428 + if (IS_REACHABLE(CONFIG_HWMON)) { 1429 + struct device *hwmon; 1430 + 1431 + hwmon = devm_hwmon_device_register_with_info(dev, "wsa883x", 1432 + wsa883x, 1433 + &wsa883x_hwmon_chip_info, 1434 + NULL); 1435 + if (IS_ERR(hwmon)) 1436 + return dev_err_probe(dev, PTR_ERR(hwmon), 1437 + "Failed to register hwmon sensor\n"); 1438 + } 1439 + 1608 1440 pm_runtime_set_autosuspend_delay(dev, 3000); 1609 1441 pm_runtime_use_autosuspend(dev); 1610 1442 pm_runtime_mark_last_busy(dev);