Skip to content

Commit

Permalink
rp3: Raspberry Pi V1 camera support with the libcamera stack
Browse files Browse the repository at this point in the history
Build libcamera and rpicam-apps from the Raspberry Pi foundation and
apply the OV5647 overlay.

Relevant documentation:

* https://forums.raspberrypi.com/viewtopic.php?t=362707
* https://www.raspberrypi.com/documentation/computers/camera_software.html
  • Loading branch information
wagdav committed Aug 4, 2024
1 parent 4dce9c5 commit 26fe213
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 4 deletions.
2 changes: 1 addition & 1 deletion host-rp3.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
imports = [
nixos-hardware.nixosModules.raspberry-pi-3
./hardware/rp3.nix
./modules/camera.nix
./modules/camera-rpi-v1
./modules/common.nix
./modules/cachix.nix
./modules/remote-builder
Expand Down
161 changes: 161 additions & 0 deletions modules/camera-rpi-v1/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Raspberry Pi V1 camera module (OV5647)
{ config, pkgs, ... }:

let

# Workaround from https://github.com/NixOS/nixos-hardware/blob/master/raspberry-pi/4/apply-overlays-dtmerge.nix
deviceTree_overlay = _final: prev: {
deviceTree = {
applyOverlays = prev.callPackage ./overlays/apply-overlays-dtmerge.nix { };
compileDTS = prev.deviceTree.compileDTS;
};
};

libcamera_overlay = (import ./overlays/libcamera.nix);

rpicam-apps_overlay = _final: prev: {
rpicam-apps = prev.callPackage ./rpicam-apps.nix { };
};

in

{
nixpkgs.overlays = [
deviceTree_overlay
libcamera_overlay
rpicam-apps_overlay
];

environment.systemPackages = with pkgs; [
rpicam-apps
];

hardware.deviceTree.filter = "bcm2837-rpi-3*";
hardware.deviceTree.overlays = [
# Equivalent to:
# https://github.com/raspberrypi/linux/blob/rpi-6.1.y/arch/arm/boot/dts/overlays/ov5647-overlay.dts
#
# As proposed in https://github.com/NixOS/nixpkgs/issues/125354, the "compatible" attribute is changed to bcm2837.
{
name = "ov5647-overlay";
dtsText = ''
// SPDX-License-Identifier: GPL-2.0-only
// Definitions for OV5647 camera module on VC I2C bus
/dts-v1/;
/plugin/;
/{
compatible = "brcm,bcm2837";
i2c_frag: fragment@0 {
target = <&i2c_csi_dsi>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
cam_node: ov5647@36 {
compatible = "ovti,ov5647";
reg = <0x36>;
status = "disabled";
clocks = <&cam1_clk>;
avdd-supply = <&cam1_reg>;
dovdd-supply = <&cam_dummy_reg>;
dvdd-supply = <&cam_dummy_reg>;
rotation = <0>;
orientation = <2>;
port {
cam_endpoint: endpoint {
clock-lanes = <0>;
data-lanes = <1 2>;
clock-noncontinuous;
link-frequencies =
/bits/ 64 <297000000>;
};
};
};
vcm_node: ad5398@c {
compatible = "adi,ad5398";
reg = <0x0c>;
status = "disabled";
VANA-supply = <&cam1_reg>;
};
};
};
csi_frag: fragment@1 {
target = <&csi1>;
csi: __overlay__ {
status = "okay";
brcm,media-controller;
port {
csi_ep: endpoint {
remote-endpoint = <&cam_endpoint>;
data-lanes = <1 2>;
};
};
};
};
fragment@2 {
target = <&i2c0if>;
__overlay__ {
status = "okay";
};
};
fragment@3 {
target = <&i2c0mux>;
__overlay__ {
status = "okay";
};
};
reg_frag: fragment@4 {
target = <&cam1_reg>;
__overlay__ {
startup-delay-us = <20000>;
};
};
clk_frag: fragment@5 {
target = <&cam1_clk>;
__overlay__ {
status = "okay";
clock-frequency = <25000000>;
};
};
__overrides__ {
rotation = <&cam_node>,"rotation:0";
orientation = <&cam_node>,"orientation:0";
media-controller = <&csi>,"brcm,media-controller?";
cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
<&csi_frag>, "target:0=",<&csi0>,
<&reg_frag>, "target:0=",<&cam0_reg>,
<&clk_frag>, "target:0=",<&cam0_clk>,
<&cam_node>, "clocks:0=",<&cam0_clk>,
<&cam_node>, "avdd-supply:0=",<&cam0_reg>,
<&vcm_node>, "VANA-supply:0=",<&cam0_reg>;
vcm = <&vcm_node>, "status=okay",
<&cam_node>,"lens-focus:0=", <&vcm_node>;
};
};
&cam_node {
status = "okay";
};
&cam_endpoint {
remote-endpoint = <&csi_ep>;
};
'';
}
];
}
50 changes: 50 additions & 0 deletions modules/camera-rpi-v1/overlays/apply-overlays-dtmerge.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# modification of nixpkgs deviceTree.applyOverlays to resolve https://github.com/NixOS/nixpkgs/issues/125354
# derived from https://github.com/NixOS/nixpkgs/blob/916ca8f2b0c208def051f8ea9760c534a40309db/pkgs/os-specific/linux/device-tree/default.nix
{ lib, stdenvNoCC, dtc, libraspberrypi }:

with lib; (base: overlays': stdenvNoCC.mkDerivation {
name = "device-tree-overlays";
nativeBuildInputs = [ dtc libraspberrypi ];
buildCommand =
let
overlays = toList overlays';
in
''
mkdir -p $out
cd "${base}"
find . -type f -name '*.dtb' -print0 \
| xargs -0 cp -v --no-preserve=mode --target-directory "$out" --parents
for dtb in $(find "$out" -type f -name '*.dtb'); do
dtbCompat=$(fdtget -t s "$dtb" / compatible 2>/dev/null || true)
# skip files without `compatible` string
test -z "$dtbCompat" && continue
${flip (concatMapStringsSep "\n") overlays (o: ''
overlayCompat="$(fdtget -t s "${o.dtboFile}" / compatible)"
# skip incompatible and non-matching overlays
if [[ ! "$dtbCompat" =~ "$overlayCompat" ]]; then
echo "Skipping overlay ${o.name}: incompatible with $(basename "$dtb")"
elif ${if ((o.filter or null) == null) then "false" else ''
[[ "''${dtb//${o.filter}/}" == "$dtb" ]]
''}
then
echo "Skipping overlay ${o.name}: filter does not match $(basename "$dtb")"
else
echo -n "Applying overlay ${o.name} to $(basename "$dtb")... "
mv "$dtb"{,.in}
# dtmerge requires a .dtbo ext for dtbo files, otherwise it adds it to the given file implicitly
dtboWithExt="$TMPDIR/$(basename "${o.dtboFile}").dtbo"
cp -r ${o.dtboFile} "$dtboWithExt"
dtmerge "$dtb.in" "$dtb" "$dtboWithExt"
echo "ok"
rm "$dtb.in" "$dtboWithExt"
fi
'')}
done'';
})
40 changes: 40 additions & 0 deletions modules/camera-rpi-v1/overlays/libcamera.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
final: prev: {
libcamera = prev.libcamera.overrideAttrs (old: {
buildInputs = old.buildInputs ++ [ prev.boost prev.nlohmann_json ];
nativeBuildInputs = old.nativeBuildInputs ++ [ prev.python3Packages.pybind11 ];

BOOST_INCLUDEDIR = "${prev.lib.getDev prev.boost}/include";
BOOST_LIBRARYDIR = "${prev.lib.getLib prev.boost}/lib";

postPatch = old.postPatch + ''
patchShebangs src/py/libcamera
'';

mesonFlags = old.mesonFlags ++ [
"-Dcam=disabled"
"-Dgstreamer=disabled"
"-Dipas=rpi/vc4,rpi/pisp"
"-Dpipelines=rpi/vc4,rpi/pisp"
];

src = prev.fetchFromGitHub {
owner = "raspberrypi";
repo = "libcamera";
rev = "6ddd79b5bdbedc1f61007aed35391f1559f9e29a";
sha256 = "eFIiYCsuukPuG6iqHZeKsXQYSuZ+9q5oLNwuJJ+bAhk=";

nativeBuildInputs = [ prev.git ];

postFetch = ''
cd "$out"
export NIX_SSL_CERT_FILE=${prev.cacert}/etc/ssl/certs/ca-bundle.crt
${prev.lib.getExe prev.meson} subprojects download \
libpisp
find subprojects -type d -name .git -prune -execdir rm -r {} +
'';
};
});
}
71 changes: 71 additions & 0 deletions modules/camera-rpi-v1/rpicam-apps.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Based on https://github.com/NixOS/nixpkgs/pull/281803
{ stdenv
, fetchFromGitHub
, lib
, makeWrapper
, meson
, ninja
, pkg-config
, boost
, ffmpeg-headless
, libcamera
, libdrm
, libepoxy
, libexif
, libjpeg
, libpng
, libtiff
, libX11
}:

stdenv.mkDerivation (finalAttrs: {
pname = "rpicam-apps";
version = "1.5.0";

src = fetchFromGitHub {
owner = "raspberrypi";
repo = "rpicam-apps";
rev = "v${finalAttrs.version}";
hash = "sha256-s4zJh6r3VhiquO54KWZ78dVCH1BmlphY9zEB9BidNyo=";
};

buildInputs = [
boost
ffmpeg-headless
libcamera
libdrm
libepoxy # GLES/EGL preview window
libexif
libjpeg
libpng
libtiff
libX11
];

nativeBuildInputs = [
makeWrapper
meson
ninja
pkg-config
];

# Meson is no longer able to pick up Boost automatically.
# https://github.com/NixOS/nixpkgs/issues/86131
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";

# See all options here: https://github.com/raspberrypi/rpicam-apps/blob/main/meson_options.txt
mesonFlags = [
"-Denable_drm=disabled"
"-Denable_egl=disabled"
"-Denable_hailo=disabled"
"-Denable_qt=disabled"
];

postInstall = ''
for f in rpicam-hello rpicam-jpeg rpicam-raw rpicam-still rpicam-vid
do
wrapProgram $out/bin/$f --set-default LIBCAMERA_IPA_PROXY_PATH ${libcamera}/libexec/libcamera
done
'';
})
3 changes: 0 additions & 3 deletions modules/camera.nix

This file was deleted.

0 comments on commit 26fe213

Please sign in to comment.