summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-07-13 20:00:00 +0200
committerMel <mel@rnrd.eu>2026-07-13 20:00:00 +0200
commitdd8f085fc0232b77f3db885b0670a3e8d3094d36 (patch)
tree940d89b684dbaa5175d816ad3d1bdafc8a397e1c
parent398608948fa730883d234a636cb33f73f2da51cf (diff)
downloadmhs35-drm-main.tar.zst
mhs35-drm-main.zip
Wrap into NixOS and Nix flakeHEADmain
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--flake.nix20
-rw-r--r--module.nix117
-rw-r--r--package.nix41
3 files changed, 178 insertions, 0 deletions
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..2f3d5d3
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,20 @@
+{
+ description = "DRM driver and NixOS module for goodtft MHS-3.5 Raspberry Pi displays";
+
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
+
+ outputs = { self, nixpkgs }:
+ let
+ system = "aarch64-linux";
+ pkgs = import nixpkgs { inherit system; };
+ in
+ {
+ nixosModules.default = import ./module.nix;
+
+ # standalone kernel module.
+ # built against the default nixpkgs kernel, if you need it inside
+ # of your kernel, use the nixos module above!
+ packages.${system}.default =
+ pkgs.linuxPackages.callPackage ./package.nix { };
+ };
+}
diff --git a/module.nix b/module.nix
new file mode 100644
index 0000000..2cb054c
--- /dev/null
+++ b/module.nix
@@ -0,0 +1,117 @@
+# nixos module to fully configure mhs35 support both in kernel and userland.
+{
+ config,
+ lib,
+ ...
+}:
+
+let
+ inherit (lib)
+ mkEnableOption
+ mkIf
+ mkOption
+ optional
+ types
+ ;
+
+ cfg = config.hardware.mhs35;
+
+ mhs35-module = config.boot.kernelPackages.callPackage ./package.nix { };
+
+ # just replace the text to avoid messing with merging device trees,
+ # it's just simpler this way... :)
+ panelDts = builtins.replaceStrings
+ [ "rotation = <180>" "spi-max-frequency = <8000000>" ]
+ [
+ "rotation = <${toString cfg.rotation}>"
+ "spi-max-frequency = <${toString cfg.spiFrequencyHz}>"
+ ]
+ (builtins.readFile ./dts/mhs35-overlay.dts);
+
+ # touch matrices for panel. (libinput)
+ # TODO(mel): 90 & 270 are missing, but are of course supported.
+ # let's write the matrix out for them, too!
+ defaultMatrices = {
+ "0" = "0 1 0 -1 0 1";
+ "180" = "0 -1 1 1 0 0";
+ };
+in
+{
+ options.hardware.mhs35 = {
+ enable = mkEnableOption "goodtft MHS-3.5 SPI display support";
+
+ rotation = mkOption {
+ type = types.enum [ 0 90 180 270 ];
+ default = 180;
+ description = ''
+ The panel rotation.
+ '';
+ # orientations (for pi4, when looking at screen upright):
+ # * 0 - landscape, usb/rj-45 ports on the left
+ # * 90 - portrait, ports on top
+ # * 180 - landscape, ports on the right
+ # * 270 - portrait, ports on bottom
+ };
+
+ spiFrequencyHz = mkOption {
+ type = types.ints.positive;
+ default = 8000000;
+ description = ''
+ Panel SPI frequency.
+ 8 MHz is the very very conservative default, the panel
+ claims to support upto 125MHz.
+ '';
+ };
+
+ touch = {
+ enable = mkEnableOption "MHS-3.5 ADS7846 touch support" // {
+ default = true;
+ };
+
+ calibrationMatrix = mkOption {
+ type = types.nullOr types.str;
+ default = defaultMatrices.${toString cfg.rotation} or null;
+ description = ''
+ Touch calibration matrix for touchscreen (libinput).
+ Set as LIBINPUT_CALIBRATION_MATRIX in udev.
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !cfg.touch.enable || cfg.touch.calibrationMatrix != null;
+ message = ''
+ hardware.mhs35: no known touch calibration matrix for
+ rotation ${toString cfg.rotation}!
+ set `hardware.mhs35.touch.calibrationMatrix` explicitly.
+ (and consider contributing the matrix! :3)
+ '';
+ }
+ ];
+
+ boot.extraModulePackages = [ mhs35-module ];
+
+ # do not let the default in-tree ili9486 win control over our device!
+ # (it also lists compatibility with "waveshare,rpi-lcd-35")
+ boot.blacklistedKernelModules = [ "ili9486" ];
+
+ hardware.deviceTree.overlays = [
+ {
+ name = "mhs35-overlay";
+ dtsText = panelDts;
+ }
+ ] ++ optional cfg.touch.enable {
+ name = "mhs35-touch-overlay";
+ dtsFile = ./dts/mhs35-touch-overlay.dts;
+ };
+
+ services.libinput.enable = mkIf cfg.touch.enable true;
+
+ services.udev.extraRules = mkIf cfg.touch.enable ''
+ ATTRS{name}=="ADS7846 Touchscreen", ENV{LIBINPUT_CALIBRATION_MATRIX}="${cfg.touch.calibrationMatrix}"
+ '';
+ };
+}
diff --git a/package.nix b/package.nix
new file mode 100644
index 0000000..78ffd24
--- /dev/null
+++ b/package.nix
@@ -0,0 +1,41 @@
+# kernel module derivation.
+# use via: `config.boot.kernelPackages.callPackage ./package.nix { }`
+{
+ lib,
+ stdenv,
+ kernel,
+}:
+
+stdenv.mkDerivation {
+ pname = "mhs35";
+ version = "1.0";
+
+ src = lib.fileset.toSource {
+ root = ./.;
+ fileset = lib.fileset.unions [
+ ./mhs35.c
+ ./Kbuild
+ ];
+ };
+
+ hardeningDisable = [ "pic" ];
+ nativeBuildInputs = kernel.moduleBuildDependencies;
+
+ makeFlags = [
+ "-C"
+ "${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
+ "M=$(PWD)"
+ "modules"
+ ];
+
+ installPhase = ''
+ install -D mhs35.ko \
+ $out/lib/modules/${kernel.modDirVersion}/extra/mhs35.ko
+ '';
+
+ meta = {
+ description = "DRM driver for goodtft MHS-3.5 panels";
+ license = lib.licenses.gpl2Plus;
+ platforms = [ "aarch64-linux" ];
+ };
+}