diff --git a/flake.nix b/flake.nix index 3f46dd2..56ce0f8 100644 --- a/flake.nix +++ b/flake.nix @@ -1,23 +1,27 @@ { description = "Surface Laptop 7 (x1e80100 / Snapdragon X Elite) kernel and hardware support"; - outputs = { self }: { - nixosModules = { - default = self.nixosModules.all; + outputs = + { self }: + { + nixosModules = { + default = self.nixosModules.all; - all = { ... }: { - imports = [ - self.nixosModules.kernel - self.nixosModules.kernel-modules - self.nixosModules.boot - self.nixosModules.hardware - ]; + all = + { ... }: + { + imports = [ + self.nixosModules.kernel + self.nixosModules.kernel-modules + self.nixosModules.boot + self.nixosModules.hardware + ]; + }; + + kernel = import ./kernel.nix; + kernel-modules = import ./kernel-modules.nix; + boot = import ./boot.nix; + hardware = import ./hardware.nix; }; - - kernel = import ./kernel.nix; - kernel-modules = import ./kernel-modules.nix; - boot = import ./boot.nix; - hardware = import ./hardware.nix; }; - }; } diff --git a/hardware.nix b/hardware.nix index cd636a7..e5f86fe 100644 --- a/hardware.nix +++ b/hardware.nix @@ -1,13 +1,23 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let - dtbName = { - "13" = "qcom/x1e80100-microsoft-romulus13.dtb"; - "15" = "qcom/x1e80100-microsoft-romulus15.dtb"; - }.${config.x1e.model}; + dtbName = + { + "13" = "qcom/x1e80100-microsoft-romulus13.dtb"; + "15" = "qcom/x1e80100-microsoft-romulus15.dtb"; + } + .${config.x1e.model}; in { options.x1e.model = lib.mkOption { - type = lib.types.enum [ "13" "15" ]; + type = lib.types.enum [ + "13" + "15" + ]; default = "13"; description = "Surface Laptop 7 display size (13.8\" or 15\")."; }; diff --git a/kernel-modules.nix b/kernel-modules.nix index 669afc0..ec911eb 100644 --- a/kernel-modules.nix +++ b/kernel-modules.nix @@ -1,252 +1,288 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let - kernel = config.boot.kernelPackages.kernel; + kernel = config.boot.kernelPackages.kernel; - spi-hid = pkgs.stdenv.mkDerivation { - pname = "spi-hid"; - version = "0.3.1-${kernel.version}"; - src = ./kernel/modules/spi-hid; - hardeningDisable = [ - "pic" - "format" - ]; - nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; - makeFlags = [ - "KERNELRELEASE=${kernel.modDirVersion}" - "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" - "INSTALL_MOD_PATH=$(out)" - ]; - buildPhase = '' - runHook preBuild - make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ - M=$(pwd) \ - ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ - modules - runHook postBuild - ''; - installPhase = '' - runHook preInstall - install -D -m 644 spi-hid.ko $out/lib/modules/${kernel.modDirVersion}/extra/spi-hid.ko - runHook postInstall - ''; - enableParallelBuilding = true; - meta = with lib; { - description = "HID over SPI (HIDSPI v3) QSPI transport driver"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; + spi-hid = pkgs.stdenv.mkDerivation { + pname = "spi-hid"; + version = "0.3.1-${kernel.version}"; + src = ./kernel/modules/spi-hid; + hardeningDisable = [ + "pic" + "format" + ]; + nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; + makeFlags = [ + "KERNELRELEASE=${kernel.modDirVersion}" + "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" + "INSTALL_MOD_PATH=$(out)" + ]; + buildPhase = '' + runHook preBuild + make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ + M=$(pwd) \ + ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ + modules + runHook postBuild + ''; + installPhase = '' + runHook preInstall + install -D -m 644 spi-hid.ko $out/lib/modules/${kernel.modDirVersion}/extra/spi-hid.ko + runHook postInstall + ''; + enableParallelBuilding = true; + meta = with lib; { + description = "HID over SPI (HIDSPI v3) QSPI transport driver"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; + ath12k-norfkill = pkgs.stdenv.mkDerivation { + pname = "ath12k-norfkill"; + inherit (kernel) + src + version + postPatch + nativeBuildInputs + ; + kernel_dev = kernel.dev; + kernelVersion = kernel.modDirVersion; + patches = [ + ./kernel/modules/ath12k/disable-rfkill.patch + ]; + buildPhase = '' + BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build + cp $BUILT_KERNEL/Module.symvers . + cp $BUILT_KERNEL/.config . + cp $kernel_dev/vmlinux . + make "-j$NIX_BUILD_CORES" modules_prepare + make "-j$NIX_BUILD_CORES" M=drivers/net/wireless/ath/ath12k modules + ''; + installPhase = '' + install -D -m 644 drivers/net/wireless/ath/ath12k/ath12k.ko \ + $out/lib/modules/${kernel.modDirVersion}/extra/ath12k.ko + if [ -f drivers/net/wireless/ath/ath12k/wifi7/ath12k_wifi7.ko ]; then + install -D -m 644 drivers/net/wireless/ath/ath12k/wifi7/ath12k_wifi7.ko \ + $out/lib/modules/${kernel.modDirVersion}/extra/ath12k_wifi7.ko + fi + ''; + meta = with lib; { + description = "ath12k with rfkill config early-return workaround"; + license = licenses.bsd3; + platforms = platforms.linux; + }; + }; - ath12k-norfkill = pkgs.stdenv.mkDerivation { - pname = "ath12k-norfkill"; - inherit (kernel) - src - version - postPatch - nativeBuildInputs - ; - kernel_dev = kernel.dev; - kernelVersion = kernel.modDirVersion; - patches = [ - ./kernel/modules/ath12k/disable-rfkill.patch - ]; - buildPhase = '' - BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build - cp $BUILT_KERNEL/Module.symvers . - cp $BUILT_KERNEL/.config . - cp $kernel_dev/vmlinux . - make "-j$NIX_BUILD_CORES" modules_prepare - make "-j$NIX_BUILD_CORES" M=drivers/net/wireless/ath/ath12k modules - ''; - installPhase = '' - install -D -m 644 drivers/net/wireless/ath/ath12k/ath12k.ko \ - $out/lib/modules/${kernel.modDirVersion}/extra/ath12k.ko - if [ -f drivers/net/wireless/ath/ath12k/wifi7/ath12k_wifi7.ko ]; then - install -D -m 644 drivers/net/wireless/ath/ath12k/wifi7/ath12k_wifi7.ko \ - $out/lib/modules/${kernel.modDirVersion}/extra/ath12k_wifi7.ko - fi - ''; - meta = with lib; { - description = "ath12k with rfkill config early-return workaround"; - license = licenses.bsd3; - platforms = platforms.linux; - }; - }; + gpi-qspi = pkgs.stdenv.mkDerivation { + pname = "gpi-qspi"; + inherit (kernel) + src + version + postPatch + nativeBuildInputs + ; + kernel_dev = kernel.dev; + kernelVersion = kernel.modDirVersion; + patches = [ + ./kernel/modules/qcom-qspi/gpi.patch + ]; + buildPhase = '' + BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build + cp $BUILT_KERNEL/Module.symvers . + cp $BUILT_KERNEL/.config . + cp $kernel_dev/vmlinux . + make "-j$NIX_BUILD_CORES" modules_prepare + make "-j$NIX_BUILD_CORES" M=drivers/dma/qcom modules + ''; + installPhase = '' + make \ + INSTALL_MOD_PATH="$out" \ + XZ="xz -T$NIX_BUILD_CORES" \ + M="drivers/dma/qcom" \ + modules_install + ''; + meta = with lib; { + description = "Qualcomm GPI DMA with QSPI protocol 9 support"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; - gpi-qspi = pkgs.stdenv.mkDerivation { - pname = "gpi-qspi"; - inherit (kernel) - src - version - postPatch - nativeBuildInputs - ; - kernel_dev = kernel.dev; - kernelVersion = kernel.modDirVersion; - patches = [ - ./kernel/modules/qcom-qspi/gpi.patch - ]; - buildPhase = '' - BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build - cp $BUILT_KERNEL/Module.symvers . - cp $BUILT_KERNEL/.config . - cp $kernel_dev/vmlinux . - make "-j$NIX_BUILD_CORES" modules_prepare - make "-j$NIX_BUILD_CORES" M=drivers/dma/qcom modules - ''; - installPhase = '' - make \ - INSTALL_MOD_PATH="$out" \ - XZ="xz -T$NIX_BUILD_CORES" \ - M="drivers/dma/qcom" \ - modules_install - ''; - meta = with lib; { - description = "Qualcomm GPI DMA with QSPI protocol 9 support"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; + spi-geni-qcom-qspi = pkgs.stdenv.mkDerivation { + pname = "spi-geni-qcom-qspi"; + inherit (kernel) + src + version + postPatch + nativeBuildInputs + ; + kernel_dev = kernel.dev; + kernelVersion = kernel.modDirVersion; + patches = [ + ./kernel/modules/qcom-qspi/gpi.patch + ./kernel/modules/qcom-qspi/spi-geni-qcom.patch + ]; + buildPhase = '' + BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build + cp $BUILT_KERNEL/Module.symvers . + cp $BUILT_KERNEL/.config . + cp $kernel_dev/vmlinux . + make "-j$NIX_BUILD_CORES" modules_prepare + make "-j$NIX_BUILD_CORES" M=drivers/spi CONFIG_SPI_QCOM_GENI=m + ''; + installPhase = '' + install -D -m 644 drivers/spi/spi-geni-qcom.ko \ + $out/lib/modules/${kernel.modDirVersion}/extra/spi-geni-qcom.ko + ''; + meta = with lib; { + description = "Qualcomm GENI SPI with QSPI 1-4-4 support"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; - spi-geni-qcom-qspi = pkgs.stdenv.mkDerivation { - pname = "spi-geni-qcom-qspi"; - inherit (kernel) - src - version - postPatch - nativeBuildInputs - ; - kernel_dev = kernel.dev; - kernelVersion = kernel.modDirVersion; - patches = [ - ./kernel/modules/qcom-qspi/gpi.patch - ./kernel/modules/qcom-qspi/spi-geni-qcom.patch - ]; - buildPhase = '' - BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build - cp $BUILT_KERNEL/Module.symvers . - cp $BUILT_KERNEL/.config . - cp $kernel_dev/vmlinux . - make "-j$NIX_BUILD_CORES" modules_prepare - make "-j$NIX_BUILD_CORES" M=drivers/spi CONFIG_SPI_QCOM_GENI=m - ''; - installPhase = '' - install -D -m 644 drivers/spi/spi-geni-qcom.ko \ - $out/lib/modules/${kernel.modDirVersion}/extra/spi-geni-qcom.ko - ''; - meta = with lib; { - description = "Qualcomm GENI SPI with QSPI 1-4-4 support"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; + cpu-parking = pkgs.stdenv.mkDerivation { + pname = "cpu-parking"; + version = "0.1.0-${kernel.version}"; + src = ./kernel/modules/cpu-parking; + hardeningDisable = [ + "pic" + "format" + ]; + nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; + makeFlags = [ + "KERNELRELEASE=${kernel.modDirVersion}" + "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" + "INSTALL_MOD_PATH=$(out)" + ]; + buildPhase = '' + runHook preBuild + make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ + M=$(pwd) \ + ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ + modules + runHook postBuild + ''; + installPhase = '' + runHook preInstall + install -D -m 644 cpu_parking.ko $out/lib/modules/${kernel.modDirVersion}/extra/cpu_parking.ko + runHook postInstall + ''; + enableParallelBuilding = true; + meta = with lib; { + description = "CPU core parking for Snapdragon X Elite (x1e80100)"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; - cpu-parking = pkgs.stdenv.mkDerivation { - pname = "cpu-parking"; - version = "0.1.0-${kernel.version}"; - src = ./kernel/modules/cpu-parking; - hardeningDisable = [ - "pic" - "format" - ]; - nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; - makeFlags = [ - "KERNELRELEASE=${kernel.modDirVersion}" - "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" - "INSTALL_MOD_PATH=$(out)" - ]; - buildPhase = '' - runHook preBuild - make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ - M=$(pwd) \ - ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ - modules - runHook postBuild - ''; - installPhase = '' - runHook preInstall - install -D -m 644 cpu_parking.ko $out/lib/modules/${kernel.modDirVersion}/extra/cpu_parking.ko - runHook postInstall - ''; - enableParallelBuilding = true; - meta = with lib; { - description = "CPU core parking for Snapdragon X Elite (x1e80100)"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; + ec-reboot = pkgs.stdenv.mkDerivation { + pname = "ec-reboot"; + version = "0.1.0-${kernel.version}"; + src = ./kernel/modules/ec-reboot; + hardeningDisable = [ + "pic" + "format" + ]; + nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; + makeFlags = [ + "KERNELRELEASE=${kernel.modDirVersion}" + "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" + "INSTALL_MOD_PATH=$(out)" + ]; + buildPhase = '' + runHook preBuild + make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ + M=$(pwd) \ + ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ + modules + runHook postBuild + ''; + installPhase = '' + runHook preInstall + install -D -m 644 ec-reboot.ko $out/lib/modules/${kernel.modDirVersion}/extra/ec-reboot.ko + runHook postInstall + ''; + enableParallelBuilding = true; + meta = with lib; { + description = "Trigger EC hard reset via SSAM for Surface Laptop 7"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; - ec-reboot = pkgs.stdenv.mkDerivation { - pname = "ec-reboot"; - version = "0.1.0-${kernel.version}"; - src = ./kernel/modules/ec-reboot; - hardeningDisable = [ - "pic" - "format" - ]; - nativeBuildInputs = kernel.moduleBuildDependencies ++ [ pkgs.kmod ]; - makeFlags = [ - "KERNELRELEASE=${kernel.modDirVersion}" - "KERNEL_DIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" - "INSTALL_MOD_PATH=$(out)" - ]; - buildPhase = '' - runHook preBuild - make -C ${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \ - M=$(pwd) \ - ARCH=${pkgs.stdenv.hostPlatform.linuxArch} \ - modules - runHook postBuild - ''; - installPhase = '' - runHook preInstall - install -D -m 644 ec-reboot.ko $out/lib/modules/${kernel.modDirVersion}/extra/ec-reboot.ko - runHook postInstall - ''; - enableParallelBuilding = true; - meta = with lib; { - description = "Trigger EC hard reset via SSAM for Surface Laptop 7"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; + platform-profile = pkgs.stdenv.mkDerivation { + pname = "platform-profile-no-acpi"; + inherit (kernel) + src + version + postPatch + nativeBuildInputs + ; + kernel_dev = kernel.dev; + kernelVersion = kernel.modDirVersion; + patches = [ + ./kernel/modules/platform-profile/platform-profile-no-acpi.patch + ]; + buildPhase = '' + BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build + cp $BUILT_KERNEL/Module.symvers . + cp $BUILT_KERNEL/.config . + cp $kernel_dev/vmlinux . + make "-j$NIX_BUILD_CORES" modules_prepare + make "-j$NIX_BUILD_CORES" M=drivers/acpi modules + ''; + installPhase = '' + make \ + INSTALL_MOD_PATH="$out" \ + XZ="xz -T$NIX_BUILD_CORES" \ + M="drivers/acpi" \ + modules_install + ''; + meta = with lib; { + description = "Platform profile module patched for DT-based systems"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; - platform-profile = pkgs.stdenv.mkDerivation { - pname = "platform-profile-no-acpi"; - inherit (kernel) - src - version - postPatch - nativeBuildInputs - ; - kernel_dev = kernel.dev; - kernelVersion = kernel.modDirVersion; - patches = [ - ./kernel/modules/platform-profile/platform-profile-no-acpi.patch - ]; - buildPhase = '' - BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build - cp $BUILT_KERNEL/Module.symvers . - cp $BUILT_KERNEL/.config . - cp $kernel_dev/vmlinux . - make "-j$NIX_BUILD_CORES" modules_prepare - make "-j$NIX_BUILD_CORES" M=drivers/acpi modules - ''; - installPhase = '' - make \ - INSTALL_MOD_PATH="$out" \ - XZ="xz -T$NIX_BUILD_CORES" \ - M="drivers/acpi" \ - modules_install - ''; - meta = with lib; { - description = "Platform profile module patched for DT-based systems"; - license = licenses.gpl2Only; - platforms = platforms.linux; - }; - }; - in + msm-vrr = pkgs.stdenv.mkDerivation { + pname = "msm-vrr"; + inherit (kernel) + src + version + postPatch + nativeBuildInputs + ; + kernel_dev = kernel.dev; + kernelVersion = kernel.modDirVersion; + patches = [ + ./kernel/modules/msm-vrr/msm-vrr-avr.patch + ]; + buildPhase = '' + BUILT_KERNEL=$kernel_dev/lib/modules/$kernelVersion/build + cp $BUILT_KERNEL/Module.symvers . + cp $BUILT_KERNEL/.config . + cp $kernel_dev/vmlinux . + make "-j$NIX_BUILD_CORES" modules_prepare + make "-j$NIX_BUILD_CORES" M=drivers/gpu/drm/msm modules + ''; + installPhase = '' + install -D -m 644 drivers/gpu/drm/msm/msm.ko \ + $out/lib/modules/${kernel.modDirVersion}/extra/msm.ko + ''; + meta = with lib; { + description = "MSM DRM with VRR (24-120Hz) for the eDP panel"; + license = licenses.gpl2Only; + platforms = platforms.linux; + }; + }; +in { options.x1e = { cpuParking = lib.mkEnableOption "CPU core parking for Snapdragon X Elite"; @@ -260,12 +296,12 @@ let spi-geni-qcom-qspi ath12k-norfkill platform-profile + msm-vrr ] ++ lib.optional config.x1e.ecReboot ec-reboot ++ lib.optional config.x1e.cpuParking cpu-parking; boot.kernelModules = - lib.optional config.x1e.ecReboot "ec_reboot" - ++ lib.optional config.x1e.cpuParking "cpu_parking"; + lib.optional config.x1e.ecReboot "ec_reboot" ++ lib.optional config.x1e.cpuParking "cpu_parking"; }; } diff --git a/kernel/modules/msm-vrr/msm-vrr-avr.patch b/kernel/modules/msm-vrr/msm-vrr-avr.patch new file mode 100644 index 0000000..2b68538 --- /dev/null +++ b/kernel/modules/msm-vrr/msm-vrr-avr.patch @@ -0,0 +1,288 @@ +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h 2026-05-29 11:03:42.924716071 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h 2026-05-29 21:59:52.331868636 +0400 +@@ -314,6 +314,7 @@ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25), ++ .features = BIT(DPU_INTF_AVR), + }, { + .name = "intf_1", .id = INTF_1, + .base = 0x35000, .len = 0x300, +@@ -348,6 +349,7 @@ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 20), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 21), ++ .features = BIT(DPU_INTF_AVR), + }, { + .name = "intf_5", .id = INTF_5, + .base = 0x39000, .len = 0x280, +@@ -356,6 +358,7 @@ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 22), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 23), ++ .features = BIT(DPU_INTF_AVR), + }, { + .name = "intf_6", .id = INTF_6, + .base = 0x3A000, .len = 0x280, +@@ -364,6 +367,7 @@ + .prog_fetch_lines_worst_case = 24, + .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16), + .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17), ++ .features = BIT(DPU_INTF_AVR), + }, { + .name = "intf_7", .id = INTF_7, + .base = 0x3b000, .len = 0x280, +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c 2026-05-29 11:03:40.286201235 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c 2026-05-29 21:59:52.333730506 +0400 +@@ -1508,6 +1508,9 @@ + (!clone_mode_requested && clone_mode_enabled)) + new_crtc_state->mode_changed = true; + ++ if (new_crtc_state->vrr_enabled != old_crtc_state->vrr_enabled) ++ new_crtc_state->mode_changed = true; ++ + return 0; + } + +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 2026-05-29 11:03:39.718213467 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c 2026-05-29 21:59:52.334011092 +0400 +@@ -1268,6 +1268,20 @@ + } + + phys->cached_mode = crtc_state->adjusted_mode; ++ ++ phys->vrr_enabled = false; ++ phys->vrr_min_fps = 0; ++ if (crtc_state->vrr_enabled && conn_state->connector) { ++ const struct drm_monitor_range_info *range = ++ &conn_state->connector->display_info.monitor_range; ++ u32 mode_fps = drm_mode_vrefresh(&crtc_state->adjusted_mode); ++ ++ if (range->min_vfreq && range->min_vfreq < mode_fps) { ++ phys->vrr_enabled = true; ++ phys->vrr_min_fps = range->min_vfreq; ++ } ++ } ++ + if (phys->ops.atomic_mode_set) + phys->ops.atomic_mode_set(phys, crtc_state, conn_state); + } +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 2026-05-29 11:11:43.623735790 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h 2026-05-29 21:59:52.334868630 +0400 +@@ -198,6 +198,8 @@ + wait_queue_head_t pending_kickoff_wq; + unsigned int irq[INTR_IDX_MAX]; + bool has_intf_te; ++ bool vrr_enabled; ++ u32 vrr_min_fps; + }; + + static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys) +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c 2026-05-29 11:03:42.399672045 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c 2026-05-29 21:59:52.336293281 +0400 +@@ -314,6 +314,18 @@ + spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); + phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, + &timing_params, fmt); ++ ++ if (phys_enc->vrr_enabled && phys_enc->hw_intf->ops.setup_avr) { ++ struct dpu_hw_intf_avr_params avr = { ++ .default_fps = drm_mode_vrefresh(&mode), ++ .min_fps = phys_enc->vrr_min_fps, ++ }; ++ ++ phys_enc->hw_intf->ops.setup_avr(phys_enc->hw_intf, ++ &timing_params, &avr); ++ phys_enc->hw_intf->ops.avr_ctrl(phys_enc->hw_intf, true, false); ++ } ++ + phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); + + /* setup which pp blk will connect to this intf */ +@@ -664,6 +676,9 @@ + spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + phys_enc->enable_state = DPU_ENC_ENABLED; + } ++ ++ if (phys_enc->vrr_enabled && phys_enc->hw_intf->ops.avr_trigger) ++ phys_enc->hw_intf->ops.avr_trigger(phys_enc->hw_intf); + } + + static void dpu_encoder_phys_vid_irq_enable(struct dpu_encoder_phys *phys_enc) +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h 2026-05-29 11:03:41.855674728 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h 2026-05-29 21:59:52.336489789 +0400 +@@ -150,6 +150,11 @@ + DPU_DSC_MAX + }; + ++enum { ++ DPU_INTF_AVR = 0x1, ++ DPU_INTF_MAX ++}; ++ + /** + * MACRO DPU_HW_BLK_INFO - information of HW blocks inside DPU + * @name: string name for debug purposes +@@ -519,6 +524,7 @@ + unsigned int intr_underrun; + unsigned int intr_vsync; + unsigned int intr_tear_rd_ptr; ++ unsigned long features; + }; + + /** +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c 2026-05-29 11:03:40.787734617 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c 2026-05-29 21:59:52.336679956 +0400 +@@ -77,6 +77,9 @@ + #define INTF_AVR_MODE 0x274 + #define INTF_AVR_TRIGGER 0x278 + #define INTF_AVR_VTOTAL 0x27C ++ ++#define INTF_AVR_CONTROL_ENABLE BIT(0) ++#define INTF_AVR_MODE_ONESHOT (BIT(0) | BIT(8)) + #define INTF_TEAR_MDP_VSYNC_SEL 0x280 + #define INTF_TEAR_TEAR_CHECK_EN 0x284 + #define INTF_TEAR_SYNC_CONFIG_VSYNC 0x288 +@@ -585,6 +588,47 @@ + DPU_REG_WRITE(&intf->hw, INTF_CONFIG2, intf_cfg2); + } + ++static void dpu_hw_intf_setup_avr(struct dpu_hw_intf *intf, ++ const struct dpu_hw_intf_timing_params *p, ++ const struct dpu_hw_intf_avr_params *avr) ++{ ++ struct dpu_hw_blk_reg_map *c = &intf->hw; ++ u32 hsync_period, vsync_period, add_porches = 0, avr_vtotal; ++ u32 diff_fps; ++ ++ if (!avr->min_fps || !avr->default_fps || avr->min_fps > avr->default_fps) ++ return; ++ ++ diff_fps = avr->default_fps - avr->min_fps; ++ ++ hsync_period = p->hsync_pulse_width + p->h_back_porch + ++ p->width + p->h_front_porch; ++ vsync_period = p->vsync_pulse_width + p->v_back_porch + ++ p->height + p->v_front_porch; ++ ++ if (diff_fps) ++ add_porches = mult_frac(vsync_period, diff_fps, avr->min_fps); ++ ++ avr_vtotal = (vsync_period + add_porches) * hsync_period; ++ ++ DPU_REG_WRITE(c, INTF_AVR_VTOTAL, avr_vtotal); ++} ++ ++static void dpu_hw_intf_avr_ctrl(struct dpu_hw_intf *intf, bool enable, bool oneshot) ++{ ++ struct dpu_hw_blk_reg_map *c = &intf->hw; ++ u32 avr_ctrl = enable ? INTF_AVR_CONTROL_ENABLE : 0; ++ u32 avr_mode = (enable && oneshot) ? INTF_AVR_MODE_ONESHOT : 0; ++ ++ DPU_REG_WRITE(c, INTF_AVR_CONTROL, avr_ctrl); ++ DPU_REG_WRITE(c, INTF_AVR_MODE, avr_mode); ++} ++ ++static void dpu_hw_intf_avr_trigger(struct dpu_hw_intf *intf) ++{ ++ DPU_REG_WRITE(&intf->hw, INTF_AVR_TRIGGER, 0x1); ++} ++ + /** + * dpu_hw_intf_init() - Initializes the INTF driver for the passed + * interface catalog entry. +@@ -652,5 +696,11 @@ + if (mdss_rev->core_major_ver >= 7) + c->ops.program_intf_cmd_cfg = dpu_hw_intf_program_intf_cmd_cfg; + ++ if (cfg->features & BIT(DPU_INTF_AVR)) { ++ c->ops.setup_avr = dpu_hw_intf_setup_avr; ++ c->ops.avr_ctrl = dpu_hw_intf_avr_ctrl; ++ c->ops.avr_trigger = dpu_hw_intf_avr_trigger; ++ } ++ + return c; + } +diff -ruN a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h 2026-05-29 11:03:41.261180349 +0400 ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.h 2026-05-29 21:59:52.337013151 +0400 +@@ -37,6 +37,11 @@ + bool compression_en; + }; + ++struct dpu_hw_intf_avr_params { ++ u32 default_fps; ++ u32 min_fps; ++}; ++ + struct dpu_hw_intf_prog_fetch { + u8 enable; + /* vsync counter for the front porch pixel line */ +@@ -111,6 +116,14 @@ + + void (*program_intf_cmd_cfg)(struct dpu_hw_intf *intf, + struct dpu_hw_intf_cmd_mode_cfg *cmd_mode_cfg); ++ ++ void (*setup_avr)(struct dpu_hw_intf *intf, ++ const struct dpu_hw_intf_timing_params *p, ++ const struct dpu_hw_intf_avr_params *avr); ++ ++ void (*avr_ctrl)(struct dpu_hw_intf *intf, bool enable, bool oneshot); ++ ++ void (*avr_trigger)(struct dpu_hw_intf *intf); + }; + + struct dpu_hw_intf { +diff -ruN a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c +--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c 2026-05-29 11:03:37.530261047 +0400 ++++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c 2026-05-29 21:59:52.337233654 +0400 +@@ -1639,6 +1639,9 @@ + if (drm_dp_max_downspread(dpcd)) + encoding[0] |= DP_SPREAD_AMP_0_5; + ++ if (dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_MSA_TIMING_PAR_IGNORED) ++ encoding[0] |= DP_MSA_TIMING_PAR_IGNORE_EN; ++ + /* config DOWNSPREAD_CTRL and MAIN_LINK_CHANNEL_CODING_SET */ + drm_dp_dpcd_write(ctrl->aux, DP_DOWNSPREAD_CTRL, encoding, 2); + +diff -ruN a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c +--- a/drivers/gpu/drm/msm/dp/dp_drm.c 2026-05-29 11:03:38.667667124 +0400 ++++ b/drivers/gpu/drm/msm/dp/dp_drm.c 2026-05-29 21:59:52.337393023 +0400 +@@ -376,6 +376,8 @@ + if (!msm_dp_display->is_edp) + drm_connector_attach_dp_subconnector_property(connector); + ++ drm_connector_attach_vrr_capable_property(connector); ++ + drm_connector_attach_encoder(connector, encoder); + + return connector; +diff -ruN a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c +--- a/drivers/gpu/drm/msm/dp/dp_panel.c 2026-05-29 11:03:39.173225245 +0400 ++++ b/drivers/gpu/drm/msm/dp/dp_panel.c 2026-05-29 21:59:52.337516414 +0400 +@@ -289,6 +289,17 @@ + } + } + ++ if (connector->vrr_capable_property) { ++ const struct drm_monitor_range_info *range = ++ &connector->display_info.monitor_range; ++ bool capable = range->min_vfreq && range->max_vfreq && ++ range->min_vfreq < range->max_vfreq && ++ (msm_dp_panel->dpcd[DP_DOWN_STREAM_PORT_COUNT] & ++ DP_MSA_TIMING_PAR_IGNORED); ++ ++ drm_connector_set_vrr_capable_property(connector, capable); ++ } ++ + end: + return rc; + }