From a688dfcbb560173cb50bf566860c12f5f41f671c Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Sun, 8 Mar 2026 18:49:33 -0700 Subject: [PATCH] raspberry-pi: add config.txt generation module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new files in raspberry-pi/common/: config-txt.nix lets you declare config.txt contents through hardware.raspberry-pi.configtxt.settings (RFC 42 style — plain values, null to remove, no enable/value wrappers). config-txt-defaults.nix has the same defaults as Raspberry Pi OS: vc4-kms-v3d, audio on, arm_boost, CM4 otg_mode, CM5 dwc2, etc. All mkDefault so they are easy to override. Not wired into board profiles yet — that comes with the RPi 5 sub-module work. Based on nvmd/nixos-raspberrypi. --- raspberry-pi/common/config-txt-defaults.nix | 29 +++++ raspberry-pi/common/config-txt.nix | 130 ++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 raspberry-pi/common/config-txt-defaults.nix create mode 100644 raspberry-pi/common/config-txt.nix diff --git a/raspberry-pi/common/config-txt-defaults.nix b/raspberry-pi/common/config-txt-defaults.nix new file mode 100644 index 00000000..af19249e --- /dev/null +++ b/raspberry-pi/common/config-txt-defaults.nix @@ -0,0 +1,29 @@ +# Default config.txt settings matching the official Raspberry Pi OS defaults. +# +# Source: https://github.com/RPi-Distro/pi-gen/blob/master/stage1/00-boot-files/files/config.txt +# All values use mkDefault so they can be easily overridden. +# +# Reference: https://www.raspberrypi.com/documentation/computers/config_txt.html + +{ lib, ... }: + +{ + hardware.raspberry-pi.configtxt.settings = { + all = { + camera_auto_detect = lib.mkDefault true; + display_auto_detect = lib.mkDefault true; + max_framebuffers = lib.mkDefault 2; + disable_fw_kms_setup = lib.mkDefault true; + disable_overscan = lib.mkDefault true; + arm_boost = lib.mkDefault true; + dtparam = lib.mkDefault [ "audio=on" ]; + dtoverlay = lib.mkDefault [ "vc4-kms-v3d" ]; + }; + cm4 = { + otg_mode = lib.mkDefault true; + }; + cm5 = { + dtoverlay = lib.mkDefault [ "dwc2,dr_mode=host" ]; + }; + }; +} diff --git a/raspberry-pi/common/config-txt.nix b/raspberry-pi/common/config-txt.nix new file mode 100644 index 00000000..568a0e1d --- /dev/null +++ b/raspberry-pi/common/config-txt.nix @@ -0,0 +1,130 @@ +# config.txt generation module for Raspberry Pi +# +# Generates config.txt from structured Nix options. The type system models +# config.txt as nested attrs: each nesting level adds a conditional filter, +# and leaves are config values. Repeated keys (dtparam, dtoverlay, gpio) +# are supported via lists. +# +# Based on work from nvmd/nixos-raspberrypi (MIT License) and rendering +# approach suggested by @quentinmit. Follows RFC 42. +# +# Reference: https://www.raspberrypi.com/documentation/computers/config_txt.html + +{ lib, config, ... }: + +let + cfg = config.hardware.raspberry-pi.configtxt; + + mkValueString = + v: + if builtins.isInt v then + toString v + else if builtins.isString v then + v + else if true == v then + "1" + else if false == v then + "0" + else + builtins.abort "config.txt: unsupported value type: ${builtins.typeOf v}"; + + mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } "="; + + # Recursively flatten nested attrs into a list of { conditionals, name, value } records. + # Each nesting level (except the leaf) is a conditional filter. + # "all" filters are omitted from the conditionals list since they reset state. + recurse = + path: value: + if builtins.isAttrs value && !(builtins.isList value) && !(value ? _type) then + lib.flatten (lib.mapAttrsToList (name: recurse ([ name ] ++ path)) value) + else + { + conditionals = lib.sort builtins.lessThan (lib.filter (k: k != "all") (lib.tail path)); + name = lib.head path; + inherit value; + }; + + # Group flattened items by their conditional filter set, then render. + groupItems = + items: + lib.mapAttrsToList (groupJSON: groupItems: { + conditionals = builtins.fromJSON groupJSON; + items = builtins.listToAttrs groupItems; + }) (builtins.groupBy (x: builtins.toJSON x.conditionals) items); + + mkGroup = + group: + lib.concatMapStrings (k: "[${k}]\n") group.conditionals + + lib.generators.toKeyValue { + inherit mkKeyValue; + listsAsDuplicateKeys = true; + } group.items; + + toConfigTxt = + attrs: + let + groups = lib.sort (a: b: a.conditionals < b.conditionals) ( + groupItems (lib.flatten (recurse [ ] attrs)) + ); + in + lib.concatMapStringsSep "\n[all]\n" mkGroup groups; + +in +{ + options.hardware.raspberry-pi.configtxt = { + settings = lib.mkOption { + type = + with lib.types; + let + atom = oneOf [ + str + int + bool + ]; + molecule = oneOf [ + atom + (listOf atom) + (attrsOf molecule) + ]; + in + attrsOf molecule; + default = { }; + description = '' + Structured configuration for the Raspberry Pi `config.txt` file. + + Top-level keys are conditional filter sections (`all`, `pi4`, `pi5`, + `cm4`, etc.). Nesting adds stacked conditional filters. Leaves are + config values rendered as `key=value`. Lists produce repeated keys. + + Booleans render as `0`/`1`. Use `null` with `mkForce` to remove a + default. + + See + ''; + example = lib.literalExpression '' + { + all = { + arm_boost = true; + disable_overscan = true; + dtparam = [ "audio=on" ]; + dtoverlay = [ "vc4-kms-v3d" ]; + }; + pi5.arm_freq = 2400; + cm4.otg_mode = true; + } + ''; + }; + + file = lib.mkOption { + type = lib.types.str; + description = '' + The generated config.txt content. Defaults to the rendered output of + `settings`, but can be overridden to supply a custom config.txt. + ''; + }; + }; + + config = { + hardware.raspberry-pi.configtxt.file = lib.mkDefault (toConfigTxt cfg.settings); + }; +}