{ config, lib, pkgs, namespace, ... }: let cfg = config.${namespace}.hardware.raspberry-pi; dt_ao_overlay = _final: prev: { deviceTree = prev.deviceTree // { applyOverlays = _final.callPackage ./apply-overlays-dtmerge.nix { }; }; }; in { options.${namespace}.hardware.raspberry-pi = { enable = lib.mkEnableOption "Raspberry Pi common configuration"; variant = lib.mkOption { type = lib.types.enum [ "4" "5" ]; description = "Raspberry Pi variant (4 or 5)"; }; apply-overlays-dtmerge = { enable = lib.mkEnableOption "" // { description = '' Whether replace deviceTree.applyOverlays implementation to use dtmerge from libraspberrypi. This can resolve issues with applying dtbs for the pi. ''; }; }; }; imports = [ ./leds.nix ]; config = lib.mkIf cfg.enable { boot = { initrd.availableKernelModules = [ "usbhid" "usb-storage" ] ++ (if (cfg.variant == "5") then [ "nvme" ] else [ "vc4" "pcie-brcmstb" # required for the pcie bus to work "reset-raspberrypi" # required for vl805 firmware to load ]); loader = { generic-extlinux-compatible.enable = lib.mkDefault true; grub.enable = lib.mkForce false; }; }; # Common Raspberry Pi packages environment.systemPackages = with pkgs; [ i2c-tools raspberrypi-eeprom pkgs.${namespace}.raspberrypifw pkgs.${namespace}.raspberryPiWirelessFirmware raspberrypi-armstubs erofs-utils fex squashfuse squashfsTools ]; # Common Bluetooth configuration systemd = { services.btattach = { before = [ "bluetooth.service" ]; after = [ "dev-ttyAMA0.device" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStart = "${lib.getExe' pkgs.bluez "btattach"} -B /dev/ttyAMA0 -P bcm -S 3000000"; }; }; tmpfiles.packages = [ pkgs.${namespace}.udev-rules ]; }; # Common hardware settings hardware = { deviceTree = { filter = lib.mkDefault (if (cfg.variant == "5") then "bcm2712-rpi-*.dtb" else "bcm2711-rpi-*.dtb"); overlays = lib.mkIf (cfg.variant == "4") [ { name = "rpi4-cpu-revision"; dtsText = '' /dts-v1/; /plugin/; / { compatible = "raspberrypi,4-model-b"; fragment@0 { target-path = "/"; __overlay__ { system { linux,revision = <0x00d03114>; }; }; }; }; ''; } { name = "enable-xhci"; dtsText = '' /dts-v1/; /plugin/; / { compatible = "brcm,bcm2711"; fragment@0 { //target-path = "/scb/xhci@7e9c0000"; target = <&xhci>; __overlay__ { status = "okay"; }; }; }; ''; } ]; }; firmware = [ pkgs.${namespace}.raspberryPiWirelessFirmware ]; graphics.enable32Bit = lib.mkForce false; i2c.enable = lib.mkDefault true; }; # Pi specific system tags # system.nixos.tags = ( # let # bootCfg = config.boot.loader.raspberry-pi; # in # [ # "raspberry-pi-${bootCfg.variant}" # bootCfg.bootloader # config.boot.kernelPackages.kernel.version # ] # ); # Common programs programs.kdeconnect.enable = lib.mkDefault false; # Root user shell configuration users = { users.root.shell = pkgs.zsh; extraGroups = { gpio = {}; i2c = {}; input = {}; plugdev = {}; spi = {}; video = {}; }; }; services = { udev.packages = [ pkgs.${namespace}.udev-rules ]; xserver.extraConfig = let identifier = "rp1"; driver = "rp1-vec|rp1-dsi|rp1-dpi"; in '' Section "OutputClass" Identifier "${identifier}" MatchDriver "${driver}" Driver "modesetting" Option "PrimaryGPU" "true" EndSection ''; }; nixpkgs.overlays = [ # (_self: super: { # ffmpeg = super.${namespace}.ffmpeg-rpi; # }) # (_self: super: { # libraspberrypi = super.${namespace}.libraspberrypi; # }) # (_self: super: { # raspberrypi-utils = super.${namespace}.raspberrypi-utils; # }) ] ++ (if cfg.variant == "5" then [ (final: prev: { ubootRaspberryPi5 = prev.uboot.ubootRaspberryPi4_64Bit.override { defconfig = "rpi_5_defconfig"; }; }) # (final: prev: # let # mkPi5 = drv: # prev.lib.overrideDerivation # (drv // { # # Make sure we build the BCM2712 defconfig regardless of internal mapping # argsOverride = (drv.argsOverride or {}) // { # defconfig = "bcm2712_defconfig"; # }; # # Limit platforms to aarch64 for Pi 5 # extraMeta = (drv.extraMeta or {}) // { # platforms = with prev.lib.platforms; prev.lib.intersectLists aarch64 linux; # hydraPlatforms = [ "aarch64-linux" ]; # }; # }) # (old: { # postFixup = (old.postFixup or "") + '' # dtbDir=${if old.stdenv.hostPlatform.isAarch64 then "$out/dtbs/broadcom" else "$out/dtbs"} # copyDTB() { cp -v "$dtbDir/$1" "$dtbDir/$2"; } # # Pi 5 alias (only if your boot chain expects bcm283x-style names) # if [ -e "$dtbDir/bcm2712-rpi-5-b.dtb" ]; then # copyDTB bcm2712-rpi-5-b.dtb bcm2839-rpi-5-b.dtb # fi # ''; # }); # in { # # Assuming your package is exposed as pkgs.linux-rpi; adapt the name if different # linux-rpi5 = mkPi5 (prev.linux_rpi4.override { rpiVersion = 5; }); # }) (final: prev: { # https://github.com/nvmd/nixos-raspberrypi/issues/64 # credit for the initial version of this snippet goes to @micahcc jemalloc = prev.jemalloc.overrideAttrs (old: { # --with-lg-page=(log2 page_size) # RPi5 (bcm2712): since our page size is 16384 (2**14), we need 14 configureFlags = let pageSizeFlag = "--with-lg-page"; in (prev.lib.filter (flag: prev.lib.hasPrefix pageSizeFlag flag == false) old.configureFlags) ++ [ "${pageSizeFlag}=14" ]; }); }) ] else [ ]) ++ (if cfg.apply-overlays-dtmerge.enable then [ dt_ao_overlay ] else [ ]); }; }