···
1
1
-
# modification of nixpkgs deviceTree.applyOverlays to resolve https://github.com/NixOS/nixpkgs/issues/125354
2
2
-
# derived from https://github.com/NixOS/nixpkgs/blob/916ca8f2b0c208def051f8ea9760c534a40309db/pkgs/os-specific/linux/device-tree/default.nix
3
3
-
{ lib, stdenvNoCC, dtc, libraspberrypi }:
4
4
-
5
5
-
with lib; (base: overlays': stdenvNoCC.mkDerivation {
6
6
-
name = "device-tree-overlays";
7
7
-
nativeBuildInputs = [ dtc libraspberrypi ];
8
8
-
buildCommand =
9
9
-
let
10
10
-
overlays = toList overlays';
11
11
-
in
12
12
-
''
13
13
-
mkdir -p $out
14
14
-
cd "${base}"
15
15
-
find . -type f -name '*.dtb' -print0 \
16
16
-
| xargs -0 cp -v --no-preserve=mode --target-directory "$out" --parents
17
17
-
18
18
-
for dtb in $(find "$out" -type f -name '*.dtb'); do
19
19
-
dtbCompat=$(fdtget -t s "$dtb" / compatible 2>/dev/null || true)
20
20
-
# skip files without `compatible` string
21
21
-
test -z "$dtbCompat" && continue
22
22
-
23
23
-
${flip (concatMapStringsSep "\n") overlays (o: ''
24
24
-
overlayCompat="$(fdtget -t s "${o.dtboFile}" / compatible)"
25
25
-
26
26
-
# skip incompatible and non-matching overlays
27
27
-
if [[ ! "$dtbCompat" =~ "$overlayCompat" ]]; then
28
28
-
echo "Skipping overlay ${o.name}: incompatible with $(basename "$dtb")"
29
29
-
elif ${if ((o.filter or null) == null) then "false" else ''
30
30
-
[[ "''${dtb//${o.filter}/}" == "$dtb" ]]
31
31
-
''}
32
32
-
then
33
33
-
echo "Skipping overlay ${o.name}: filter does not match $(basename "$dtb")"
34
34
-
else
35
35
-
echo -n "Applying overlay ${o.name} to $(basename "$dtb")... "
36
36
-
mv "$dtb"{,.in}
37
37
-
38
38
-
# dtmerge requires a .dtbo ext for dtbo files, otherwise it adds it to the given file implicitly
39
39
-
dtboWithExt="$TMPDIR/$(basename "${o.dtboFile}").dtbo"
40
40
-
cp -r ${o.dtboFile} "$dtboWithExt"
41
41
-
42
42
-
dtmerge "$dtb.in" "$dtb" "$dtboWithExt"
43
43
-
44
44
-
echo "ok"
45
45
-
rm "$dtb.in" "$dtboWithExt"
46
46
-
fi
47
47
-
'')}
48
48
-
49
49
-
done'';
50
50
-
})
···
4
4
nm = config.flake.modules.nixos;
5
5
in
6
6
{
7
7
-
flake.nixosConfigurations.pi = inputs.nixpkgs.lib.nixosSystem {
8
8
-
system = "aarch64-linux";
7
7
+
flake.nixosConfigurations.pi = inputs.nixos-raspberrypi.lib.nixosSystem {
9
8
modules = [
10
10
-
"${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
11
11
-
inputs.nixos-hardware.nixosModules.raspberry-pi-4
9
9
+
{ imports = [
10
10
+
inputs.nixos-raspberrypi.nixosModules.raspberry-pi-4.base
11
11
+
inputs.nixos-raspberrypi.nixosModules.sd-image
12
12
+
];
13
13
+
}
12
14
inputs.agenix.nixosModules.default
13
13
-
14
14
-
# Allow missing kernel modules
15
15
-
({
16
16
-
nixpkgs.overlays = [
17
17
-
(final: super: {
18
18
-
makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; });
19
19
-
})
20
20
-
];
21
21
-
})
22
15
23
16
# Aspect modules
24
17
nm.pi-camera
···
36
29
height = 1080;
37
30
};
38
31
framerate = 30;
39
39
-
deviceTreeFilter = "bcm2711-rpi-4*.dtb";
40
40
-
deviceTreeCompatible = "brcm,bcm2711";
41
32
gpuMem = 256;
42
33
};
43
34
···
4
4
nm = config.flake.modules.nixos;
5
5
in
6
6
{
7
7
-
flake.nixosConfigurations.pizero = inputs.nixpkgs.lib.nixosSystem {
8
8
-
system = "aarch64-linux";
7
7
+
flake.nixosConfigurations.pizero = inputs.nixos-raspberrypi.lib.nixosSystem {
9
8
modules = [
10
10
-
"${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
11
11
-
inputs.agenix.nixosModules.default
12
12
-
13
13
-
# Allow missing kernel modules
14
14
-
({
15
15
-
nixpkgs.overlays = [
16
16
-
(final: super: {
17
17
-
makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; });
18
18
-
})
9
9
+
{ imports = [
10
10
+
inputs.nixos-raspberrypi.nixosModules.raspberry-pi-02.base
11
11
+
inputs.nixos-raspberrypi.nixosModules.sd-image
19
12
];
20
20
-
})
13
13
+
}
14
14
+
inputs.agenix.nixosModules.default
21
15
22
16
# Aspect modules
23
17
nm.pi-camera
···
25
19
nm.pi-stability
26
20
27
21
# Pi Zero 2W specific settings
28
28
-
(
29
29
-
{ pkgs, lib, ... }:
30
30
-
{
31
31
-
networking.hostName = "pizero";
22
22
+
{
23
23
+
networking.hostName = "pizero";
32
24
33
33
-
pi = {
34
34
-
streamName = "pizerocam";
35
35
-
resolution = {
36
36
-
width = 1280;
37
37
-
height = 720;
38
38
-
};
39
39
-
framerate = 15;
40
40
-
deviceTreeFilter = "bcm2837-rpi-zero-2-w.dtb";
41
41
-
deviceTreeCompatible = "brcm,bcm2837";
42
42
-
gpuMem = 128;
43
43
-
flipCamera = true;
25
25
+
pi = {
26
26
+
streamName = "pizerocam";
27
27
+
resolution = {
28
28
+
width = 1280;
29
29
+
height = 720;
44
30
};
45
45
-
46
46
-
# Use RPi kernel which includes IMX708 camera driver
47
47
-
boot.kernelPackages = pkgs.linuxPackages_rpi3;
48
48
-
49
49
-
# Disable NixOS DTB so U-Boot uses the firmware's DTB
50
50
-
hardware.deviceTree.enable = lib.mkForce false;
51
51
-
sdImage.populateFirmwareCommands = lib.mkAfter ''
52
52
-
chmod u+w ./firmware/config.txt
53
53
-
echo "dtoverlay=imx708" >> ./firmware/config.txt
54
54
-
echo "camera_auto_detect=1" >> ./firmware/config.txt
55
55
-
'';
31
31
+
framerate = 15;
32
32
+
gpuMem = 128;
33
33
+
flipCamera = true;
34
34
+
};
56
35
57
57
-
system.stateVersion = "24.11";
58
58
-
}
59
59
-
)
36
36
+
system.stateVersion = "24.11";
37
37
+
}
60
38
];
61
39
};
62
40
}
···
3
3
{ pkgs, lib, config, ... }:
4
4
let
5
5
cfg = config.pi;
6
6
-
7
7
-
deviceTree_overlay = _final: prev: {
8
8
-
deviceTree = {
9
9
-
applyOverlays = prev.callPackage ../hosts/pi-common/overlays/apply-overlays-dtmerge.nix { };
10
10
-
compileDTS = prev.deviceTree.compileDTS;
11
11
-
};
12
12
-
};
13
13
-
14
14
-
libcamera-rpi = pkgs.libcamera.overrideAttrs (old: {
15
15
-
mesonFlags = (old.mesonFlags or [ ]) ++ [
16
16
-
"-Dipas=rpi/vc4,rpi/pisp"
17
17
-
"-Dpipelines=rpi/vc4,rpi/pisp"
18
18
-
];
19
19
-
});
20
20
-
21
21
-
rpicam-apps = pkgs.stdenv.mkDerivation rec {
22
22
-
pname = "rpicam-apps";
23
23
-
version = "1.11.1";
24
24
-
25
25
-
src = pkgs.fetchFromGitHub {
26
26
-
owner = "raspberrypi";
27
27
-
repo = "rpicam-apps";
28
28
-
rev = "v${version}";
29
29
-
hash = "sha256-hVoKbvWFeramPkHuibJwUgFOPS9v588+K8828a1fNnA=";
30
30
-
};
31
31
-
32
32
-
nativeBuildInputs = with pkgs; [
33
33
-
meson
34
34
-
ninja
35
35
-
pkg-config
36
36
-
];
37
37
-
38
38
-
buildInputs = [
39
39
-
libcamera-rpi
40
40
-
pkgs.libdrm
41
41
-
pkgs.libexif
42
42
-
pkgs.libjpeg
43
43
-
pkgs.libpng
44
44
-
pkgs.libtiff
45
45
-
pkgs.boost
46
46
-
pkgs.ffmpeg
47
47
-
];
48
48
-
49
49
-
mesonFlags = [
50
50
-
"-Denable_libav=enabled"
51
51
-
"-Denable_drm=enabled"
52
52
-
"-Denable_egl=disabled"
53
53
-
"-Denable_qt=disabled"
54
54
-
"-Denable_opencv=disabled"
55
55
-
"-Denable_tflite=disabled"
56
56
-
"-Denable_hailo=disabled"
57
57
-
];
58
58
-
59
59
-
meta = with lib; {
60
60
-
description = "Raspberry Pi camera applications";
61
61
-
homepage = "https://github.com/raspberrypi/rpicam-apps";
62
62
-
license = licenses.bsd2;
63
63
-
platforms = [ "aarch64-linux" ];
64
64
-
};
65
65
-
};
66
6
in
67
7
{
68
8
options.pi = {
···
87
27
default = 30;
88
28
description = "Camera framerate";
89
29
};
90
90
-
deviceTreeFilter = lib.mkOption {
91
91
-
type = lib.types.str;
92
92
-
description = "Device tree filter pattern";
93
93
-
};
94
94
-
deviceTreeCompatible = lib.mkOption {
95
95
-
type = lib.types.str;
96
96
-
description = "Device tree compatible string";
97
97
-
};
98
30
gpuMem = lib.mkOption {
99
31
type = lib.types.int;
100
32
default = 256;
101
33
description = "GPU memory allocation in MB";
102
34
};
103
103
-
cmaMem = lib.mkOption {
104
104
-
type = lib.types.int;
105
105
-
default = 256;
106
106
-
description = "CMA (Contiguous Memory Allocator) size in MB for DMA buffers";
107
107
-
};
108
35
flipCamera = lib.mkOption {
109
36
type = lib.types.bool;
110
37
default = false;
···
113
40
};
114
41
115
42
config = {
116
116
-
# CMA is configured via device tree overlay (cma-256m)
117
117
-
# Do NOT use boot.kernelParams cma= as it bypasses the DT node
118
118
-
# and prevents /dev/dma_heap/linux,cma from being created
119
119
-
120
43
nix.settings.trusted-users = [ "sean" ];
121
44
122
45
networking.useDHCP = true;
123
123
-
nixpkgs.overlays = [ deviceTree_overlay ];
124
124
-
125
125
-
boot.supportedFilesystems = lib.mkForce [
126
126
-
"vfat"
127
127
-
"ext4"
128
128
-
];
129
129
-
boot.initrd.systemd.enable = false;
130
46
131
47
services.openssh = {
132
48
enable = true;
···
150
66
151
67
security.sudo.wheelNeedsPassword = false;
152
68
69
69
+
# Firmware config.txt: CMA and GPU memory
70
70
+
hardware.raspberry-pi.config.all = {
71
71
+
options.gpu_mem = {
72
72
+
enable = true;
73
73
+
value = cfg.gpuMem;
74
74
+
};
75
75
+
dt-overlays.vc4-kms-v3d.params.cma-256.enable = true;
76
76
+
};
77
77
+
153
78
services.go2rtc = {
154
79
enable = true;
155
80
settings = {
156
81
ffmpeg.bin = "${pkgs.ffmpeg}/bin/ffmpeg";
157
82
streams = {
158
158
-
"${cfg.streamName}" = "exec:${rpicam-apps}/bin/rpicam-vid -t 0 --width ${toString cfg.resolution.width} --height ${toString cfg.resolution.height} --framerate ${toString cfg.framerate} --codec h264 --inline --autofocus-mode continuous${lib.optionalString cfg.flipCamera " --vflip --hflip"} -o -";
83
83
+
"${cfg.streamName}" = "exec:${pkgs.rpi.rpicam-apps}/bin/rpicam-vid -t 0 --width ${toString cfg.resolution.width} --height ${toString cfg.resolution.height} --framerate ${toString cfg.framerate} --codec h264 --inline --autofocus-mode continuous${lib.optionalString cfg.flipCamera " --vflip --hflip"} -o -";
159
84
};
160
85
};
161
86
};
···
169
94
SupplementaryGroups = [ "video" ];
170
95
};
171
96
systemd.services.go2rtc.environment = {
172
172
-
LIBCAMERA_IPA_MODULE_PATH = "${libcamera-rpi}/lib/libcamera/ipa";
97
97
+
LIBCAMERA_IPA_MODULE_PATH = "${pkgs.rpi.libcamera}/lib/libcamera/ipa";
173
98
};
174
99
175
100
environment.systemPackages = [
176
101
pkgs.ffmpeg
177
177
-
pkgs.libraspberrypi
178
178
-
libcamera-rpi
179
179
-
rpicam-apps
102
102
+
pkgs.rpi.libcamera
103
103
+
pkgs.rpi.rpicam-apps
180
104
pkgs.v4l-utils
181
105
];
182
182
-
183
183
-
hardware.deviceTree.filter = cfg.deviceTreeFilter;
184
184
-
hardware.deviceTree.overlays = [
185
185
-
{
186
186
-
# Increase CMA from 64MB to 256MB via device tree
187
187
-
# (cmdline cma= bypasses the DT node and /dev/dma_heap/linux,cma isn't created)
188
188
-
name = "cma-256m";
189
189
-
dtsText = ''
190
190
-
/dts-v1/;
191
191
-
/plugin/;
192
192
-
193
193
-
/ {
194
194
-
compatible = "${cfg.deviceTreeCompatible}";
195
195
-
196
196
-
fragment@99 {
197
197
-
target-path = "/reserved-memory/linux,cma";
198
198
-
__overlay__ {
199
199
-
size = <0x10000000>; /* 256MB */
200
200
-
};
201
201
-
};
202
202
-
};
203
203
-
'';
204
204
-
}
205
205
-
{
206
206
-
name = "imx708-overlay";
207
207
-
dtsText = ''
208
208
-
// SPDX-License-Identifier: GPL-2.0-only
209
209
-
// Definitions for IMX708 camera module on VC I2C bus
210
210
-
/dts-v1/;
211
211
-
/plugin/;
212
212
-
213
213
-
/{
214
214
-
compatible = "${cfg.deviceTreeCompatible}";
215
215
-
216
216
-
fragment@0 {
217
217
-
target = <&i2c0if>;
218
218
-
__overlay__ {
219
219
-
status = "okay";
220
220
-
};
221
221
-
};
222
222
-
223
223
-
clk_frag: fragment@1 {
224
224
-
target = <&cam1_clk>;
225
225
-
__overlay__ {
226
226
-
status = "okay";
227
227
-
clock-frequency = <24000000>;
228
228
-
};
229
229
-
};
230
230
-
231
231
-
fragment@2 {
232
232
-
target = <&i2c0mux>;
233
233
-
__overlay__ {
234
234
-
status = "okay";
235
235
-
};
236
236
-
};
237
237
-
238
238
-
reg_frag: fragment@3 {
239
239
-
target = <&cam1_reg>;
240
240
-
cam_reg: __overlay__ {
241
241
-
startup-delay-us = <70000>;
242
242
-
off-on-delay-us = <30000>;
243
243
-
regulator-min-microvolt = <2700000>;
244
244
-
regulator-max-microvolt = <2700000>;
245
245
-
};
246
246
-
};
247
247
-
248
248
-
i2c_frag: fragment@100 {
249
249
-
target = <&i2c_csi_dsi>;
250
250
-
__overlay__ {
251
251
-
#address-cells = <1>;
252
252
-
#size-cells = <0>;
253
253
-
status = "okay";
254
254
-
255
255
-
cam_node: imx708@1a {
256
256
-
compatible = "sony,imx708";
257
257
-
reg = <0x1a>;
258
258
-
status = "okay";
259
259
-
260
260
-
clocks = <&cam1_clk>;
261
261
-
clock-names = "inclk";
262
262
-
263
263
-
vana1-supply = <&cam1_reg>;
264
264
-
vana2-supply = <&cam_dummy_reg>;
265
265
-
vdig-supply = <&cam_dummy_reg>;
266
266
-
vddl-supply = <&cam_dummy_reg>;
267
267
-
268
268
-
rotation = <180>;
269
269
-
orientation = <2>;
270
270
-
271
271
-
port {
272
272
-
cam_endpoint: endpoint {
273
273
-
clock-lanes = <0>;
274
274
-
data-lanes = <1 2>;
275
275
-
clock-noncontinuous;
276
276
-
link-frequencies =
277
277
-
/bits/ 64 <450000000>;
278
278
-
};
279
279
-
};
280
280
-
};
281
281
-
282
282
-
vcm_node: dw9817@c {
283
283
-
compatible = "dongwoon,dw9817-vcm";
284
284
-
reg = <0x0c>;
285
285
-
status = "okay";
286
286
-
VDD-supply = <&cam1_reg>;
287
287
-
};
288
288
-
};
289
289
-
};
290
290
-
291
291
-
csi_frag: fragment@101 {
292
292
-
target = <&csi1>;
293
293
-
csi: __overlay__ {
294
294
-
status = "okay";
295
295
-
brcm,media-controller;
296
296
-
297
297
-
port {
298
298
-
csi_ep: endpoint {
299
299
-
remote-endpoint = <&cam_endpoint>;
300
300
-
clock-lanes = <0>;
301
301
-
data-lanes = <1 2>;
302
302
-
clock-noncontinuous;
303
303
-
};
304
304
-
};
305
305
-
};
306
306
-
};
307
307
-
308
308
-
__overrides__ {
309
309
-
rotation = <&cam_node>,"rotation:0";
310
310
-
orientation = <&cam_node>,"orientation:0";
311
311
-
media-controller = <&csi>,"brcm,media-controller?";
312
312
-
cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
313
313
-
<&csi_frag>, "target:0=",<&csi0>,
314
314
-
<&clk_frag>, "target:0=",<&cam0_clk>,
315
315
-
<®_frag>, "target:0=",<&cam0_reg>,
316
316
-
<&cam_node>, "clocks:0=",<&cam0_clk>,
317
317
-
<&cam_node>, "vana1-supply:0=",<&cam0_reg>,
318
318
-
<&vcm_node>, "VDD-supply:0=",<&cam0_reg>;
319
319
-
vcm = <&vcm_node>, "status",
320
320
-
<0>, "=4";
321
321
-
link-frequency = <&cam_endpoint>,"link-frequencies#0";
322
322
-
};
323
323
-
};
324
324
-
325
325
-
&cam_endpoint {
326
326
-
remote-endpoint = <&csi_ep>;
327
327
-
};
328
328
-
'';
329
329
-
}
330
330
-
];
331
331
-
332
332
-
hardware.enableRedistributableFirmware = true;
333
333
-
334
334
-
sdImage.populateFirmwareCommands = lib.mkAfter ''
335
335
-
chmod u+w ./firmware/config.txt
336
336
-
cat >> ./firmware/config.txt << EOF
337
337
-
338
338
-
# Camera support - Pi Camera v3 (IMX708)
339
339
-
dtoverlay=imx708
340
340
-
gpu_mem=${toString cfg.gpuMem}
341
341
-
dtoverlay=cma,cma-${toString cfg.cmaMem}
342
342
-
EOF
343
343
-
344
344
-
if [ -d ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ]; then
345
345
-
cp -r ${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays ./firmware/
346
346
-
fi
347
347
-
'';
348
106
349
107
networking.firewall.allowedTCPPorts = [
350
108
22