Do you have any photos of it running?
It never crossed my mind to do make record it or take a photo. What would you like to see?
Just a video of you running it with default settings. Just curious in general
Latest release with some improvements, found here.
If you like it please star it.
UPD: the original message was linking a PR I sent against nixos-hardware, asking for testing; since then, upstream have proposed a cleaner fix. I have amended the instructions below to suggest testing the upstream fix instead.
I’m trying to confirm/finalize a fix for a Framework Laptop 16 (AMD AI 300) audio routing issue, and I would appreciate some help from other FW16 owners.
Symptom (before)
- GNOME / PipeWire defaults output to “Headphones” even when nothing is plugged in (and I don’t even have a 3.5mm expansion module).
- Speakers work only after manually switching output to “Speaker”.
- Similarly, the microphone defaults to “Stereo Microphone”, which is silent; the second source, “Digital Microphone”, works normally.
Root cause hypothesis
This looks like a alsa-ucm-conf issue, where “Headphones” and the extra microphone get created even when the corresponding physical hardware does not exist, and WirePlumber picks the higher-priority route.
Proposed fix
The upstream have proposed a fix. It would be good to collect some feedback from FW16 (AMD AI 300) owners who can test analog headset/headphones behavior (if you have a 3.5mm expansion card / adapter / anything else that exercises analog jack paths).
1) Do you have the issue?
Please run (with no BT/USB headsets connected if possible):
wpctl status -n
If you see the defaults as something like:
├─ Sinks:
│ * 59. alsa_output.pci-0000_c2_00.6.HiFi__Headphones__sink [vol: 0.40]
│
├─ Sources:
│ * 60. alsa_input.pci-0000_c2_00.6.HiFi__Mic2__source [vol: 1.00]
│ 61. alsa_input.pci-0000_c2_00.6.HiFi__Mic1__source [vol: 1.00]
even though you have nothing plugged in (no headphones/mic), that’s the issue I’m targeting.
2) Try the fix (NixOS flakes easiest)
If you’re using flakes and have nixos-hardware as an input, point it at the fix branch and rebuild.
Example (flake input change):
nixos-hardware.url = "github:dniku/nixos-hardware/fw16-ai300-upstream-ucm-fix";
Then:
sudo nixos-rebuild switch --flake .#YOUR_HOSTNAME
After switching, you may need to clear cached selections for default outputs:
rm -rf ~/.local/state/wireplumber
systemctl --user restart wireplumber pipewire pipewire-pulse
If you don’t want to clear them, you can instead move them aside temporarily:
mv ~/.local/state/wireplumber{,.bak}
systemctl --user restart wireplumber pipewire pipewire-pulse
Now re-run:
wpctl status -n
Finally, if you shelved your WirePlumber state, restore it:
rm -rf ~/.local/state/wireplumber
mv ~/.local/state/wireplumber{.bak,}
systemctl --user restart wireplumber pipewire pipewire-pulse
Expected (after)
$ wpctl status -n
# ...
├─ Sinks:
│ * 59. alsa_output.pci-0000_c2_00.6.HiFi__Speaker__sink [vol: 0.40]
│
├─ Sources:
│ * 60. alsa_input.pci-0000_c2_00.6.HiFi__Mic1__source [vol: 1.00]
3) What I’d like you to report (before + after)
Please paste outputs for:
nixos-version
uname -r
wpctl status -n
wpctl inspect @DEFAULT_AUDIO_SINK@
wpctl inspect @DEFAULT_AUDIO_SOURCE@
And please say:
- Your FW16 CPU (AI 300 / 7840HS / etc.)
- Whether you have any 3.5mm audio capability (expansion card/anything else) and whether headphone output + headset mic work as expected after the fix.
Links:
alsa-ucm-confissue with proposed fix: Generic HDA UCM (HDA/HiFi-analog.conf + HiFi-mic.conf) creates phantom devices on jackless Framework Laptop 16 (ALC285, f111:000d) causing wrong PipeWire defaults · Issue #673 · alsa-project/alsa-ucm-conf · GitHub- How the fix is integrated: Comparing NixOS:master...dniku:fw16-ai300-upstream-ucm-fix · NixOS/nixos-hardware · GitHub
Thanks!
Hey dniku.
Have the same problem and found your posting. I repeat the steps and here are my infos.
nixos-version
25.11.20260113.2c3e5ec (Xantusia)
uname -r
6.18.5
wpctl status -n
PipeWire 'pipewire-0' [1.4.9, peter@frmwrk, cookie:854236843]
└─ Clients:
33. pipewire [1.4.9, peter@frmwrk, pid:4039]
35. WirePlumber [1.4.9, peter@frmwrk, pid:4040]
47. WirePlumber [export] [1.4.9, peter@frmwrk, pid:4040]
64. wpctl [1.4.9, peter@frmwrk, pid:4102]
Audio
├─ Devices:
│ 48. alsa_card.pci-0000_c1_00.1 [alsa]
│ 49. alsa_card.pci-0000_c1_00.6 [alsa]
│
├─ Sinks:
│ * 56. alsa_output.pci-0000_c1_00.6.HiFi__Speaker__sink [vol: 0.40]
│
├─ Sources:
│ * 57. alsa_input.pci-0000_c1_00.6.HiFi__Mic1__source [vol: 1.00]
│
├─ Filters:
│
└─ Streams:
Video
├─ Devices:
│ 34. v4l2_device.pci-0000_c1_00.4-usb-0_1_1.0 [v4l2]
│ 46. v4l2_device.pci-0000_c1_00.4-usb-0_1_1.0.2 [v4l2]
│ 59. libcamera_device.\_SB_.PCI0.GPPA.XHC1.RHUB.PRT1-1:1.0-32ac:001c [libcamera]
│
├─ Sinks:
│
├─ Sources:
│ * 62. v4l2_input.pci-0000_c1_00.4-usb-0_1_1.0
│
├─ Filters:
│
└─ Streams:
Settings
└─ Default Configured Devices:
wpctl inspect @DEFAULT_AUDIO_SINK@
id 56, type PipeWire:Interface:Node
alsa.card = "1"
alsa.card_name = "HD-Audio Generic"
alsa.class = "generic"
alsa.components = "HDA:10ec0285,f111000d,00100002"
alsa.device = "0"
alsa.driver_name = "snd_hda_intel"
alsa.id = "ALC285 Analog"
alsa.long_card_name = "HD-Audio Generic at 0xb0bc0000 irq 141"
alsa.mixer_device = "_ucm0003.hw:Generic_1"
alsa.mixer_name = "Realtek ALC285"
alsa.name = "ALC285 Analog"
alsa.resolution_bits = "16"
alsa.subclass = "generic-mix"
alsa.subdevice = "0"
alsa.subdevice_name = "subdevice #0"
alsa.sync.id = "00000000:00000000:00000000:00000000"
api.alsa.card.longname = "HD-Audio Generic at 0xb0bc0000 irq 141"
api.alsa.card.name = "HD-Audio Generic"
api.alsa.open.ucm = "true"
api.alsa.path = "hw:Generic_1"
api.alsa.pcm.card = "1"
api.alsa.pcm.stream = "playback"
audio.channels = "2"
audio.position = "FL,FR"
card.profile.device = "0"
* client.id = "47"
clock.quantum-limit = "8192"
device.api = "alsa"
device.bus = "pci"
device.class = "sound"
device.icon-name = "audio-card-analog"
device.icon_name = "audio-speakers"
* device.id = "49"
device.profile.description = "Speaker"
device.profile.name = "HiFi: Speaker: sink"
device.routes = "1"
* factory.id = "19"
factory.name = "api.alsa.pcm.sink"
library.name = "audioconvert/libspa-audioconvert"
* media.class = "Audio/Sink"
* node.description = "Family 17h/19h/1ah HD Audio Controller Speaker"
node.driver = "true"
node.loop.name = "data-loop.0"
* node.name = "alsa_output.pci-0000_c1_00.6.HiFi__Speaker__sink"
* node.nick = "ALC285 Analog"
node.pause-on-idle = "false"
* object.path = "alsa:acp:Generic_1:0:playback"
* object.serial = "57"
port.group = "playback"
* priority.driver = "1000"
* priority.session = "1000"
wpctl inspect @DEFAULT_AUDIO_SOURCE@
id 57, type PipeWire:Interface:Node
alsa.card = "2"
alsa.card_name = "acp-pdm-mach"
alsa.class = "generic"
alsa.device = "0"
alsa.driver_name = "snd_acp_legacy_mach"
alsa.id = "DMIC capture dmic-hifi-0"
alsa.long_card_name = "Framework-Laptop16AMDRyzenAI300Series-A7-FRANMHCP07"
alsa.mixer_device = "_ucm0003.hw:Generic_1"
alsa.name = ""
alsa.resolution_bits = "32"
alsa.subclass = "generic-mix"
alsa.subdevice = "0"
alsa.subdevice_name = "subdevice #0"
alsa.sync.id = "00000000:00000000:00000000:00000000"
api.alsa.card.longname = "HD-Audio Generic at 0xb0bc0000 irq 141"
api.alsa.card.name = "HD-Audio Generic"
api.alsa.open.ucm = "true"
api.alsa.path = "hw:acppdmmach"
api.alsa.pcm.card = "1"
api.alsa.pcm.stream = "capture"
audio.channels = "2"
audio.position = "FL,FR"
card.profile.device = "1"
* client.id = "47"
clock.quantum-limit = "8192"
device.api = "alsa"
device.bus = "pci"
device.class = "sound"
device.icon-name = "audio-card-analog"
device.icon_name = "audio-input-microphone"
* device.id = "49"
device.profile.description = "Digital Microphone"
device.profile.name = "HiFi: Mic1: source"
device.routes = "1"
* factory.id = "19"
factory.name = "api.alsa.pcm.source"
library.name = "audioconvert/libspa-audioconvert"
* media.class = "Audio/Source"
* node.description = "Family 17h/19h/1ah HD Audio Controller Digital Microphone"
node.driver = "true"
node.loop.name = "data-loop.0"
* node.name = "alsa_input.pci-0000_c1_00.6.HiFi__Mic1__source"
* node.nick = "Digital Microphone"
node.pause-on-idle = "false"
* object.path = "alsa:acp:Generic_1:1:capture"
* object.serial = "58"
port.group = "capture"
* priority.driver = "2000"
* priority.session = "2000"
- My FW16 CPU is a AI300
- I have no 3.5mm audio capability and no
- It works as exptected after the fix
I hope this helps.
Best regards
Peter
Thanks Peter! I’ll see whether I can add this override directly to nixos-hardware, or if nixpkgs can pick up the newest version of alsa-ucm-conf which already includes the fix.
So FW16 seems to be covered; FW13, unfortunately, is still broken: https://github.com/NixOS/nixos-hardware/issues/1603
Hey @dniku
Is there a workaround for this issue?
You can use my nixos-hardware fork right now; once nixpkgs update alsa-ucm-conf to ≥ v1.2.15.2 or once I merge the override you tried into nixos-hardware you can revert to using the regular upstream.
nixpkgs now has a pending PR to bump alsa-ucm-conf to v1.2.15.3: NixOS/nixpkgs/pull/483379
(for some reason if I try to post the full link I get “sorry, you cannot post links to that host”)
Hey everyone, I just want to share my repo of NixOS and Home-manager modules that I created to workaround issues and improve quality of life using NixOS, Home-manager, and Hyprland on my Framework 16. Most of these modules are power-management related.
I had this issue:
So I created fw16-disable-wake-triggers as one-shot systemd service to disable wake triggers except the power button. Not ideal, but it works for my setup.
I saw this suggestion:
So I created fw16-kbd-alsd a systemd daemon to automatically control the keyboard backlight using the ALS.
There are also:
lidmond – Lid monitor daemon: A systemd service designed to replace logind (for controlling lid events) with more customizable and flexible control.
hyprlidmon – Hyprland lid monitor: The Home-manager service companion for lidmond (makes use of hyprctl for monitor control).
batmond – Battery monitor daemon: A systemd user daemon that displays customizable messages and runs customizable commands when different levels of battery discharge are detected. It supports running in both graphical and non-graphical (tty) environments.
powerproud – Powerprofiles user daemon: A systemd user daemon that automatically switches power-profiles and adjusts screen brightness based on the battery charging status. Requires power-profiles-daemon to function.
hyprSuspendBlocker – Hyprland suspend blocker: A script to block systemctl suspend given user specified conditions. Intended for use with hypridle.
Hope someone can get some use out of these. If not, I’m going to continue using them in my config anyway.
-Pete