Re: [RFC v4 0/4] platform/x86/amd: Add AMD DPTCi driver for TDP control in devices without vendor-specific controls
From: Antheas Kapenekakis
Date: Tue Mar 10 2026 - 04:03:57 EST
On Tue, 10 Mar 2026 at 03:43, Mario Limonciello
<mario.limonciello@xxxxxxx> wrote:
>
>
>
> On 3/9/2026 3:51 PM, Antheas Kapenekakis wrote:
> > Many AMD-based handheld PCs (GPD, AYANEO, OneXPlayer, AOKZOE, OrangePi)
> > ship with the AGESA ALIB method at \_SB.ALIB, which accepts Function 0x0C
> > (the Dynamic Power and Thermal Configuration Interface, DPTCi). This
> > allows software to adjust APU power and thermal parameters at runtime:
> > sustained power limit (SPL/STAPM + skin), slow PPT, fast PPT, and the
> > thermal control target.
> >
> > Unlike mainstream AMD laptops, these devices do not implement vendor-
> > specific WMI or EC hooks for TDP control. The ones that do, use DPTCi
> > under the hood. For these devices, ALIB is the only viable mechanism for
> > the OS to adjust power limits, making a dedicated kernel driver the
> > correct approach rather than relying on the out-of-tree acpi_call module
> > or ryzenadj.
> >
> > The driver provides two layers of control:
> >
> > * Platform profile integration (low-power / balanced / performance /
> > custom), with per-device preset tunings derived from thermal envelope
> > data. Selecting a non-custom profile applies values immediately and
> > locks the individual tunables to read-only. The default profile is
> > "custom", leaving the device at firmware defaults until userspace
> > explicitly selects a profile.
> >
> > * Four firmware-attributes tunables (ppt_pl1_spl, ppt_pl2_sppt,
> > ppt_pl3_fppt, cpu_temp) that become writable in "custom" mode.
> > Values are enforced against per-device limits (smin..smax), with an
> > "expanded_limits" toggle that widens the range to the full hardware-
> > validated envelope (min..max). A save_settings attribute controls
> > whether writes commit immediately ("single") or are staged for an
> > explicit bulk "save".
> >
> > On resume, the active profile or staged values are re-applied so that
> > suspend/resume cycles do not silently revert to firmware defaults unless
> > in custom and in bulk mode. In that case, defer to userspace.
> >
> > Device limits are supplied for GPD Win Mini / Win 4 / Win 5 / Win Max 2 /
> > Duo / Pocket 4, OrangePi NEO-01, AOKZOE A1/A2, OneXPlayer F1/2/X1/G1,
> > and numerous AYANEO models. The SoC table covers Ryzen 5000, 6000, 7040,
> > 8000, AI 9 HX 370, and the Ryzen AI MAX series.
> >
> > Tested on a GPD Win 5 (Ryzen AI MAX+ 395). Confirmed with ryzenadj -i
> > that committed values are applied to hardware, and that fast/slow PPT
> > limits are honoured under a full-CPU stress load.
> >
> > Responsible disclosure: The development of this driver is AI assisted.
> > This includes its writing, testing, reviewing, and readmes. The driver was
> > manually reviewed line by line, but there may still be small leftover
> > quirks. This is an RFC, not a final version. Let's push these tools to
> > their limits and see where it takes us.
> >
>
> I'll tell you where the most immediate limit is. Humans can't review
> RFC changes that go in daily.
>
> Over my weekend I see multiple copies of the series in my inbox. To me
> they must have obviously been garbage because you sent them so quickly
> instead of waiting for feedback. So I'll delete those from my inbox and
> review the most recent one.
>
> That's somewhat tongue in cheek. I know you're excited for this series,
> but please make sure you take time to gather and take feedback into
> account before posting another series.
It's true, I've been having a bit of fun. But the series is stabilized
now, I don't plan to send another version soon unless I messed up
something.
V3 incorporated your feedback/derek's and V4 incorporated rong's
feedback. V4 is minor, it is just nits.
> Maybe you can pass this to a robot and it
> can give you actionable feedback, but a human sitting down and reviewing
> a patch series in a serious way takes time.
I'm using this series as a testbed to make some tooling for this,
maybe I will post it soon.
> > Assisted-by: Claude:claude-opus-4-6
> >
> > ---
> >
> > Changes in v4:
> > - Align dptc_params continuation lines to opening brace
> > - Reflow all dptc_device_limits structs: expand zipped braces into
> > separate .params and .profiles blocks with proper indentation
> > - Extract enum dptc_save_mode from struct dptc_priv to a standalone
> > declaration
> > - Replace vague mutex comment with explicit list of protected members
> > - Use &buf[off + 1] form for put_unaligned_le32
> > - Fix misaligned '=' in ACPI in_params block
> > - Factor out dptc_alib_fill_param() helper to deduplicate ALIB buffer
> > construction in dptc_alib_send_one() and dptc_alib_save(); use int
> > instead of size_t for element counts throughout
> > - Consolidate dptc_current_value_store: single guard(mutex) instead of
> > two, eliminating duplicated profile check
> > - Return -EBUSY instead of -EPERM when profile is not custom
> > - Add blank line after early returns for readability
> > - Consolidate dptc_alib_save() call into dptc_apply_profile(), which
> > now returns int; simplify dptc_pp_set() and dptc_resume() callers
> > - Squash device_create formatting fix into the patch that introduced it
> > - Remove bogus "AMD Ryzen AI HX 360" SoC entry (no such model exists)
> > - Return -ENOENT instead of -EINVAL from dptc_alib_call() when no
> > parameters are staged, matching think-lmi save_settings semantics
> > - Remove bool has_staged[] array: use staged[i] == 0 as the "not
> > staged" sentinel, raise expanded_min from 0 to 1 W to ensure 0 is
> > never a valid user value
> > - Remove unnecessary braces from if/else if/else chain in dptc_resume()
> > - Drop Ryzen Z1 SoC entries: Z1 devices (Lenovo/Asus) use vendor EC/PMF
> > drivers, not ALIB DPTCi
> >
> > Changes in v3:
> > - Split single driver patch into 3: core driver, platform profile,
> > device entries
> > - Rename DRIVER_NAME from "amd_dptc" to "amd-dptc" (match subsystem
> > convention for platform drivers and firmware-attributes devices)
> > - Add scale field to dptc_param_desc: sysfs values in user units (W, C),
> > driver multiplies by scale (1000 for mW) before sending to ALIB
> > - Rename struct fields: min/smin/smax/max ->
> > expanded_min/device_min/device_max/expanded_max
> > - Remove comment "ALIB parameter IDs (AGESA spec Appendix E.5, Table
> > E-52)"
> > - Move ALIB method check after SoC/DMI validation, change pr_debug to
> > pr_warn
> > - Reorder local variable declaration in dptc_init (dptc after other vars)
> > - Add commit subject prefix "dptc:" to all driver patches
> > - Remove early-return for empty save in dptc_alib_save; let
> > dptc_alib_call handle the empty case
> > - Remove max_power as we do not do DC/AC validation in the driver
> > - Fix Ayaneo AIR device matches to reflect their wattage, add 15W profile
> > for original AIR. Cheers to the AIR Plus user who had helped tune the
> > AIR Plus profile so it was correct.
> >
> > Changes in v2:
> > - Use a platform_device base instead of raw inits + exit, hook into devm
> > helpers referencing samsung-galaxybook
> > - Add platform_profile support (low-power / balanced / performance /
> > custom) with per-device energy presets; non-custom profiles lock
> > tunables to read-only. We default to custom to avoid writing values
> > - Reduce exposed parameters from seven to four (ppt_pl1_spl,
> > ppt_pl2_sppt, ppt_pl3_fppt, cpu_temp); drop time constants and
> > separate skin/STAPM limits in favour of a single SPL that sets both.
> > For devices where the max tdp offers thermals that are not suitable
> > for day to day use (e.g., excessive fan noise), MAX_POWER is added
> > - Remove CONFIG_AMD_DPTC_EXTENDED and the "soc"/"unbound" limit tiers;
> > keep only device (smin..smax) and expanded (min..max) and soc match
> > (certain devices ship multiple SoCs on the same motherboard/thermal
> > envelope, make sure we only hook into validated SoCs)
> > - Rename "commit" attribute to "save_settings" per firmware-attributes
> > ABI; rename limit_mode to expanded_limits
> > - Change expanded_limits attribute type from "enumeration" to "integer"
> > (min=0, max=1) since firmware-attributes has no bool type
> > - Remove all global vars, limit _dev access to init and exit
> > - Use u32 accessors to set values for ALIB call
> > - Clean up verbose comments throughout
> > - Add Ayn Loki Max / Tectoy Zeenix Pro
> >
> > V3: https://lore.kernel.org/all/20260307115516.26892-1-lkml@xxxxxxxxxxx/
> > V2: https://lore.kernel.org/all/20260305181751.3642846-1-lkml@xxxxxxxxxxx/
> > V1: https://lore.kernel.org/all/20260303181707.2920261-1-lkml@xxxxxxxxxxx/
> >
> > Antheas Kapenekakis (4):
> > Documentation: firmware-attributes: generalize save_settings entry
> > platform/x86/amd: dptc: Add AMD DPTCi driver
> > platform/x86/amd: dptc: Add platform profile support
> > platform/x86/amd: dptc: Add device entries for handheld PCs
> >
> > .../testing/sysfs-class-firmware-attributes | 41 +-
> > MAINTAINERS | 6 +
> > drivers/platform/x86/amd/Kconfig | 15 +
> > drivers/platform/x86/amd/Makefile | 2 +
> > drivers/platform/x86/amd/dptc.c | 1271 +++++++++++++++++
> > 5 files changed, 1320 insertions(+), 15 deletions(-)
> > create mode 100644 drivers/platform/x86/amd/dptc.c
> >
> >
> > base-commit: 4ae12d8bd9a830799db335ee661d6cbc6597f838
>
>