[PATCH v7 7/9] media: chips-media: wave6: Add Wave6 thermal cooling device
From: Nas Chung <nas.chung@chipsnmedia.com>
Date: 2026-09-04 06:47:29
Also in:
linux-devicetree, linux-media, lkml
Subsystem:
media input infrastructure (v4l/dvb), the rest · Maintainers:
Mauro Carvalho Chehab, Linus Torvalds
Add a thermal cooling device for the Wave6 VPU. The device operates within the Linux thermal framework, adjusting the VPU performance state based on thermal conditions. Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com> Tested-by: Ming Qian <redacted> Tested-by: Marek Vasut <redacted> --- .../chips-media/wave6/wave6-vpu-thermal.c | 143 ++++++++++++++++++ .../chips-media/wave6/wave6-vpu-thermal.h | 24 +++ 2 files changed, 167 insertions(+) create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c create mode 100644 drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c
new file mode 100644
index 000000000000..1e67943b213b
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.c@@ -0,0 +1,143 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +/* + * Wave6 series multi-standard codec IP - wave6 thermal cooling interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + * + */ + +#include <linux/pm_domain.h> +#include <linux/pm_opp.h> +#include <linux/units.h> +#include <linux/slab.h> +#include "wave6-vpu-thermal.h" + +static int wave6_vpu_thermal_cooling_update(struct vpu_thermal_cooling *thermal, + int state) +{ + unsigned long new_clock_rate; + int ret; + + if (state > thermal->thermal_max || !thermal->cooling) + return 0; + + new_clock_rate = DIV_ROUND_UP(thermal->freq_table[state], HZ_PER_KHZ); + dev_dbg(thermal->dev, "receive cooling state: %d, new clock rate %ld\n", + state, new_clock_rate); + + ret = dev_pm_genpd_set_performance_state(thermal->dev, new_clock_rate); + if (ret && !((ret == -ENODEV) || (ret == -EOPNOTSUPP))) { + dev_err(thermal->dev, "failed to set perf to %lu, ret = %d\n", + new_clock_rate, ret); + return ret; + } + + return 0; +} + +static int wave6_vpu_cooling_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct vpu_thermal_cooling *thermal = cdev->devdata; + + *state = thermal->thermal_max; + + return 0; +} + +static int wave6_vpu_cooling_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct vpu_thermal_cooling *thermal = cdev->devdata; + + *state = thermal->thermal_event; + + return 0; +} + +static int wave6_vpu_cooling_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct vpu_thermal_cooling *thermal = cdev->devdata; + int ret; + + ret = wave6_vpu_thermal_cooling_update(thermal, state); + if (ret) + return ret; + + thermal->thermal_event = state; + + return 0; +} + +static const struct thermal_cooling_device_ops wave6_cooling_ops = { + .get_max_state = wave6_vpu_cooling_get_max_state, + .get_cur_state = wave6_vpu_cooling_get_cur_state, + .set_cur_state = wave6_vpu_cooling_set_cur_state, +}; + +int wave6_vpu_cooling_init(struct device *dev, struct vpu_thermal_cooling *thermal) +{ + int i; + int num_opps; + unsigned long freq; + int ret = -EINVAL; + + if (WARN_ON(!thermal || !thermal->dev)) + return -EINVAL; + + num_opps = dev_pm_opp_get_opp_count(thermal->dev); + if (num_opps < 0) { + dev_err(thermal->dev, "fail to get pm opp count, ret = %d\n", num_opps); + return num_opps; + } + if (num_opps == 0) { + dev_err(thermal->dev, "no OPP entries found\n"); + return -ENODEV; + } + + thermal->freq_table = devm_kcalloc(dev, num_opps, + sizeof(*thermal->freq_table), + GFP_KERNEL); + if (!thermal->freq_table) { + ret = -ENOMEM; + goto error; + } + + for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { + struct dev_pm_opp *opp; + + opp = dev_pm_opp_find_freq_floor(thermal->dev, &freq); + if (IS_ERR(opp)) + break; + + dev_pm_opp_put(opp); + + dev_dbg(thermal->dev, "[%d] = %lu\n", i, freq); + if (freq < 100 * HZ_PER_MHZ) + break; + + thermal->freq_table[i] = freq; + thermal->thermal_max = i; + } + + if (!thermal->thermal_max) + goto error; + + thermal->thermal_event = 0; + thermal->cooling = devm_thermal_of_child_cooling_device_register(dev, + dev->of_node, + dev_name(thermal->dev), + thermal, + &wave6_cooling_ops); + if (IS_ERR(thermal->cooling)) { + dev_err(thermal->dev, "register cooling device failed\n"); + ret = PTR_ERR(thermal->cooling); + goto error; + } + + return 0; + +error: + return ret; +}
diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h
new file mode 100644
index 000000000000..7c5e8aed6ef7
--- /dev/null
+++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-thermal.h@@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Wave6 series multi-standard codec IP - wave6 thermal cooling interface + * + * Copyright (C) 2025 CHIPS&MEDIA INC + * + */ + +#ifndef __WAVE6_VPU_THERMAL_H__ +#define __WAVE6_VPU_THERMAL_H__ + +#include <linux/thermal.h> + +struct vpu_thermal_cooling { + struct device *dev; + int thermal_event; + int thermal_max; + struct thermal_cooling_device *cooling; + unsigned long *freq_table; +}; + +int wave6_vpu_cooling_init(struct device *dev, struct vpu_thermal_cooling *thermal); + +#endif /* __WAVE6_VPU_THERMAL_H__ */
--
2.31.1