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!

2 Likes

Finally returned to this project and got the right set of configuration switches set to play audio from the speakers on my C302CA. If you are attempting this, see my config at https://github.com/joseph-long/pixie/

2 Likes

Awesome!
You should make a PR to the nixos-hardware repository, so that other people can find it easily and benefit from it :slight_smile:

Thx @joseph-long for sharing your work. Finally enjoying some audio on my Chromebook.