{ lib, pkgs, config, namespace, ... }: with lib; let inherit (lib.${namespace}) mkOpt mkBoolOpt; cfg = config.${namespace}.hardware.amd; in { options.${namespace}.hardware.amd = { enable = mkEnableOption "AMD hardware configuration"; coolercontrol.enable = mkBoolOpt false "Enable CoolerControl fan/cooling control"; corectrl = { enable = mkBoolOpt false "Enable CoreCtrl GPU control"; enablePolkit = mkBoolOpt false "Enable CoreCtrl polkit rules"; polkitGroup = mkOpt types.str "wheel" "Group allowed to use CoreCtrl without password"; }; lact.enable = mkBoolOpt false "Enable LACT daemon (AMD GPU control)"; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = !(cfg.lact.enable && cfg.corectrl.enable); message = "mjallen.hardware.amd: lact and corectrl cannot both be enabled — they both manage AMD GPU power profiles and will conflict."; } { assertion = !cfg.corectrl.enablePolkit || cfg.corectrl.polkitGroup != ""; message = "mjallen.hardware.amd.corectrl.polkitGroup must be a non-empty group name when enablePolkit is true."; } ]; boot = { kernelModules = [ "nct6775" "k10temp" ]; kernelParams = [ "amdgpu.ppfeaturemask=0xffffffff" ]; }; programs.corectrl = { enable = cfg.corectrl.enable; package = pkgs.corectrl; }; programs.coolercontrol.enable = lib.mkIf cfg.coolercontrol.enable true; environment = { variables = { AMD_VULKAN_ICD = "RADV"; STEAM_FORCE_DESKTOPUI_SCALING = "1.0"; GDK_SCALE = "1"; }; systemPackages = lib.mkIf cfg.lact.enable [ pkgs.lact ]; }; security.polkit = lib.mkIf cfg.corectrl.enablePolkit { extraConfig = '' polkit.addRule(function(action, subject) { if ((action.id == "org.corectrl.helper.init" || action.id == "org.corectrl.helperkiller.init") && subject.local == true && subject.active == true && subject.isInGroup("${cfg.corectrl.polkitGroup}")) { return polkit.Result.YES; } }); ''; }; # k10temp is listed in kernelModules above, but the module doesn't always # load early enough for sensors to be available. This service ensures it # is loaded after multi-user.target. systemd.services = { load-k10temp = { description = "Load k10temp kernel module"; script = "${pkgs.kmod}/bin/modprobe k10temp"; wantedBy = [ "multi-user.target" ]; }; lactd = lib.mkIf cfg.lact.enable { description = "AMDGPU Control Daemon"; path = with pkgs; [ bash lact ]; script = "lact daemon"; wantedBy = [ "multi-user.target" ]; after = [ "multi-user.target" ]; }; }; }; }