Trying to include custom firmware, but it doesn't appear under /run/current-system

I have an ASUS Chromebook Flip C302CA (“Cave”) and I’m trying to get audio output through headphones and the builtin speakers to work. The efforts so far are described here: https://bugzilla.kernel.org/show_bug.cgi?id=200963

I have (with the assistance of other participants in that bug) cobbled together a working configuration on Arch Linux, and I’d like to reproduce it on NixOS. I’ve written up my Cave specific customizations as /etc/nixos/cave-audio.nix:

{ config, pkgs, ... }:

let
  caveAudioFiles = pkgs.fetchzip {
    url = "https://github.com/nebulakl/cave-audio/archive/0ac059e243c8663908500ec01d7a11ee116041d9.tar.gz";
    sha256 = "0c9bdj96d3d12fyiyh3fiim47b1lhsw1pbqb52ny0sp5wh64dwl5";
  };
  crosAudioTopology = pkgs.fetchurl {
    url = "https://bugzilla.kernel.org/attachment.cgi?id=282677";
    sha256 = "0n3ycx91g98pdias9594jqllvjxwh7ib0w65gpk5siayldqxgaal";
  };
in
{

  # Required for screen brightness control:
  boot.kernelParams = [ "acpi_backlight=vendor" ];

  hardware.enableRedistributableFirmware = true;
  boot.kernelModules = [ "skl_n88l25_m98357a" "snd_soc_skl" ];
  boot.extraModprobeConfig = ''
blacklist snd_hda_intel
'';
  # Sound requires custom UCM files and topology bin:
  system.replaceRuntimeDependencies = [
    {
      original = pkgs.alsaLib;
      replacement = pkgs.alsaLib.overrideAttrs (super: {
        postFixup = ''
cp -r ${caveAudioFiles}/Google-Cave-1.0-Cave $out/share/alsa/ucm/
ln -s $out/share/alsa/ucm/Google-Cave-1.0-Cave/ $out/share/alsa/ucm/sklnau8825max
'';
      });
    }
    {
      original = pkgs.firmwareLinuxNonfree;
      replacement = pkgs.firmwareLinuxNonfree.overrideAttrs (super: {
        postInstall = ''
cp ${crosAudioTopology} $out/lib/firmware/dfw_sst.bin
echo $out/lib/firmware/dfw_sst.bin
stat $out/lib/firmware/dfw_sst.bin
cd $out/lib/firmware/intel/
rm dsp_fw_release.bin
ln -s dsp_fw_release_v969.bin dsp_fw_release.bin
'';
      });
    }
  ];
}

However, the dfw_sst.bin firmware file I extracted from ChromeOS is not making it into /run/current-system/firmware. When I consult dmesg | grep snd_soc_skl

[    4.027732] snd_soc_skl 0000:00:1f.3: bound 0000:00:02.0 (ops i915_audio_component_bind_ops [i915])
[    5.428942] snd_soc_skl 0000:00:1f.3: Direct firmware load for 9d70-CORE-COREBOOT-0-tplg.bin failed with error -2
[    5.428945] snd_soc_skl 0000:00:1f.3: tplg fw 9d70-CORE-COREBOOT-0-tplg.bin load failed with -2, falling back to dfw_sst.bin
[    5.428960] snd_soc_skl 0000:00:1f.3: Direct firmware load for dfw_sst.bin failed with error -2
[    5.428962] snd_soc_skl 0000:00:1f.3: Fallback tplg fw dfw_sst.bin load failed with -2
[    5.429037] snd_soc_skl 0000:00:1f.3: Failed to init topology!
[    5.429087] snd_soc_skl 0000:00:1f.3: ASoC: failed to probe component -2

Based on this pkgs/build-support/kernel/modules-closure.sh it appears NixOS asks each module what firmware it expects. For whatever reason, none of my loaded kernel modules report 9d70-CORE-COREBOOT-0-tplg.bin or dfw_sst.bin as a firmware file they need, so that path doesn’t get included in the firmware list. (At least, as far as I can tell from lsmod | cut -d" " -f 1 | tail -n +2 | xargs modinfo -F firmware.)

But then again, that can’t be the whole story since “dsp_fw_release” doesn’t appear in the outputs there…

Anyone know what’s missing to make this file (normally /lib/firmware/dfw_sst.bin) visible to the module / firmware loader?

I forgot to pull the files before restoring the computer to its original state, but I had them loading on a KBL chromebook.

First of all, prefer creating a package with the additional firmware files you need and include them with hardware.firmware rather than overlaying and replacing using system.replaceRuntimeDependencies for the firmwares. (Not sure about UCM, I was still working on that.)

Here’s something to get you started:

  hardware.firmware = [
    (pkgs.runCommandNoCC "firmware-audio-CAVE" {} ''
      mkdir -p $out/lib/firmware/intel
      cp ${./9d70-CORE-COREBOOT-0-tplg.bin} $out/lib/firmware/9d70-CORE-COREBOOT-0-tplg.bin
      # Not sure about that second firmware file
      cp ${./dsp_fw_release.bin} $out/lib/firmware/intel/dsp_fw_release.bin
    '')
  ];

I had to copy two files, the topology file for my harwdare, and a firmware file; I think it was dsp_fw_release.bin, but not entirely positive. The dmesg messages told me which one to load.

This is all from memory, I don’t have the machine I did that on anymore.

Cheers, hardware.firmware is what I needed.

For anyone following along at home this is /etc/nixos/cave-audio.nix:

{ config, pkgs, ... }:

let
  caveAudioFiles = pkgs.fetchzip {
    url = "https://github.com/nebulakl/cave-audio/archive/0ac059e243c8663908500ec01d7a11ee116041d9.tar.gz";
    sha256 = "0c9bdj96d3d12fyiyh3fiim47b1lhsw1pbqb52ny0sp5wh64dwl5";
  };
  crosAudioTopology = pkgs.fetchurl {
    url = "https://bugzilla.kernel.org/attachment.cgi?id=282677";
    sha256 = "0n3ycx91g98pdias9594jqllvjxwh7ib0w65gpk5siayldqxgaal";
  };
in
{

  # Required for screen brightness control:
  boot.kernelParams = [ "acpi_backlight=vendor" ];

  hardware.enableRedistributableFirmware = true;
  hardware.firmware = [
    (pkgs.runCommandNoCC "firmware-audio-CAVE" {} ''
      mkdir -p $out/lib/firmware
      cp ${crosAudioTopology} $out/lib/firmware/9d70-CORE-COREBOOT-0-tplg.bin
    '')
  ];
  boot.kernelModules = [ "skl_n88l25_m98357a" "snd_soc_skl" ];
#  boot.extraModprobeConfig = ''
#blacklist snd_hda_intel
#'';
  # Sound requires custom UCM files and topology bin:
  system.replaceRuntimeDependencies = [
    {
      original = pkgs.alsaLib;
      replacement = pkgs.alsaLib.overrideAttrs (super: {
        postFixup = ''
cp -r ${caveAudioFiles}/Google-Cave-1.0-Cave $out/share/alsa/ucm/
ln -s $out/share/alsa/ucm/Google-Cave-1.0-Cave/ $out/share/alsa/ucm/sklnau8825max
'';
      });
    }
    {
      original = pkgs.firmwareLinuxNonfree;
      replacement = pkgs.firmwareLinuxNonfree.overrideAttrs (super: {
        postInstall = ''
cd $out/lib/firmware/intel/
rm dsp_fw_release.bin
ln -s dsp_fw_release_v969.bin dsp_fw_release.bin
'';
      });
    }
  ];
}

Then in configuration.nix I have

  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      # Apply Skylake/CAVE Audio hacks
      ./cave-audio.nix
    ];

  # Enable sound.
  sound.enable = true;
  hardware.pulseaudio.enable = true;
  hardware.pulseaudio.package = pkgs.pulseaudioFull;

As described in the nebulakl/cave-audio README there’s still the matter of changing the output profile with pactl set-card-profile 0 Headphone or pactl set-card-profile 0 Speaker (the latter must be executed once to switch the output from “dummy” the first time you try to play audio). Further work required to switch automatically!

1 Like