diff options
-rw-r--r-- | sound/soc/codecs/tas2764.c | 89 | ||||
-rw-r--r-- | sound/soc/codecs/tas2764.h | 3 |
2 files changed, 92 insertions, 0 deletions
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 21c38b2394b7..97968ee3af42 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/hwmon.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/gpio/consumer.h> @@ -577,6 +578,80 @@ static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764) return 0; } +static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result) +{ + int ret, reg; + + ret = regmap_read(tas2764->regmap, TAS2764_TEMP, ®); + if (ret) + return ret; + /* + * As per datasheet, subtract 93 from raw value to get degrees + * Celsius. hwmon wants millidegrees. + * + * NOTE: The chip will initialise the TAS2764_TEMP register to + * 2.6 *C to avoid triggering temperature protection. Since the + * ADC is powered down during software shutdown, this value will + * persist until the chip is fully powered up (e.g. the PCM it's + * attached to is opened). The ADC will power down again when + * the chip is put back into software shutdown, with the last + * value sampled persisting in the ADC's register. + */ + *result = (reg - 93) * 1000; + return 0; +} + +static umode_t tas2764_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + break; + } + + return 0; +} + +static int tas2764_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct tas2764_priv *tas2764 = dev_get_drvdata(dev); + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = tas2764_read_die_temp(tas2764, val); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static const struct hwmon_channel_info *const tas2764_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops tas2764_hwmon_ops = { + .is_visible = tas2764_hwmon_is_visible, + .read = tas2764_hwmon_read, +}; + +static const struct hwmon_chip_info tas2764_hwmon_chip_info = { + .ops = &tas2764_hwmon_ops, + .info = tas2764_hwmon_info, +}; + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); @@ -825,6 +900,20 @@ static int tas2764_i2c_probe(struct i2c_client *client) } } + if (IS_REACHABLE(CONFIG_HWMON)) { + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764", + tas2764, + &tas2764_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) { + return dev_err_probe(&client->dev, PTR_ERR(hwmon), + "Failed to register temp sensor\n"); + } + } + + return devm_snd_soc_register_component(tas2764->dev, &soc_component_driver_tas2764, tas2764_dai_driver, diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 3251dc0106e0..538290ed3d92 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -117,6 +117,9 @@ #define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50) #define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51) +/* Readout Registers */ +#define TAS2764_TEMP TAS2764_REG(0x0, 0x56) + /* Clock/IRQ Settings */ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) #define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) |