raspberry-pi: add config.txt generation module

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.
This commit is contained in:
Jamie Magee 2026-03-08 18:49:33 -07:00
parent 3966ce987e
commit a688dfcbb5
2 changed files with 159 additions and 0 deletions

View file

@ -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" ];
};
};
}

View file

@ -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 <https://www.raspberrypi.com/documentation/computers/config_txt.html>
'';
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);
};
}