No audio profile for HDA Intel (sof-hda-dsp) subwoofer output on Dell XPS 15 9530, Fedora 43 KDE Plasma 6.6

Hello everyone,

I’m using a Dell XPS 15 9530 with Fedora 43 + KDE Plasma 6.6, and I’m having an audio issue:

The kernel correctly detects the CS35L41 amplifier and loads the firmware (verified with dmesg).
PipeWire / ALSA work and the outputs appear, but only in 2-channel stereo.
When playing music, the woofers don’t produce sound, only the small front speakers work.

I’ve checked:

The package alsa-ucm-conf does not exist in Fedora 43.
sof-firmware is already installed.
In /usr/share/alsa/ucm2 there are only generic profiles, nothing specific for the XPS 15 9530.

From what I understand, the hardware is active, but Fedora lacks a UCM profile to correctly route the woofers, so the bass remains silent.

My goal is to have all speakers and woofers fully working on Fedora 43.

Has anyone in the XPS 15 9530 community managed to get the woofers working on Fedora or any other Linux distro without Windows? Any steps, overrides, or UCM files for this model would be greatly appreciated.

Thanks in advance.

PipeWire EQ with Smart Filters on Fedora/KDE (XPS 15 or any laptop)

Not sure if this has been documented elsewhere, but leaving it here in case it helps someone in the future.

The problem

I wanted a working EQ for my internal speakers on Fedora 43 / KDE Plasma, with PipeWire. The catch: I needed the hardware sink to stay as the system default, so the volume OSD and keyboard shortcuts stay in sync. But I also needed all audio to pass through the EQ automatically, without having to manually route apps.

Most guides I found (including the ones floating around GitHub Gists) use node.target in playback.props to route the EQ output to the hardware. This worked at some point, but it no longer works on PipeWire 1.x / WirePlumber 0.5+. The default node policy overrides it silently and you end up with audio bypassing the EQ, or the OSD getting out of sync.

The solution: Smart Filters

WirePlumber 0.5 introduced a feature called Smart Filters. When you mark a filter-chain node with filter.smart = true, WirePlumber automatically intercepts any stream going to the default sink and routes it through the EQ transparently. The hardware stays as the default for the system, but all audio passes through the filter first. No manual stream routing needed.

The fix is just two lines added to capture.props, and removing the old node.target from playback.props.

Setup

Create ~/.config/pipewire/pipewire.conf.d/sink-eq6.conf with this content (tune the EQ bands to your liking):

context.modules = [
    { name = libpipewire-module-filter-chain
        args = {
            node.description = "Internal Speakers Equalizer Sink"
            media.name       = "Internal Speakers Equalizer Sink"
            filter.graph = {
                nodes = [
                    {
                        type  = builtin
                        name  = eq_band_1
                        label = bq_peaking
                        control = { "Freq" = 119.0 "Q" = 1.5 "Gain" = 11.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_2
                        label = bq_peaking
                        control = { "Freq" = 238.0 "Q" = 1.5 "Gain" = 2.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_3
                        label = bq_peaking
                        control = { "Freq" = 475.0 "Q" = 1.5 "Gain" = -11.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_4
                        label = bq_peaking
                        control = { "Freq" = 947.0 "Q" = 1.5 "Gain" = -11.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_5
                        label = bq_peaking
                        control = { "Freq" = 1890.0 "Q" = 1.5 "Gain" = -2.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_6
                        label = bq_peaking
                        control = { "Freq" = 3771.0 "Q" = 1.5 "Gain" = 2.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_7
                        label = bq_peaking
                        control = { "Freq" = 7524.0 "Q" = 1.5 "Gain" = 9.0 }
                    }
                    {
                        type  = builtin
                        name  = eq_band_8
                        label = bq_peaking
                        control = { "Freq" = 15012.0 "Q" = 1.5 "Gain" = 10.0 }
                    }
                ]
                links = [
                    { output = "eq_band_1:Out" input = "eq_band_2:In" }
                    { output = "eq_band_2:Out" input = "eq_band_3:In" }
                    { output = "eq_band_3:Out" input = "eq_band_4:In" }
                    { output = "eq_band_4:Out" input = "eq_band_5:In" }
                    { output = "eq_band_5:Out" input = "eq_band_6:In" }
                    { output = "eq_band_6:Out" input = "eq_band_7:In" }
                    { output = "eq_band_7:Out" input = "eq_band_8:In" }
                ]
            }
            audio.channels = 2
            audio.position = [ FL FR ]
            capture.props = {
                node.name           = "internal_speaker"
                media.class         = Audio/Sink
                filter.smart        = true
                filter.smart.target = { node.name = "alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink" }
            }
            playback.props = {
                node.name    = "internal_speaker_equalizer_output"
                node.passive = true
            }
        }
    }
]

No WirePlumber config files needed. Then restart the audio stack:

systemctl --user restart pipewire pipewire-pulse wireplumber

Verify it worked:

wpctl status

You should see your apps listed under the EQ filter, and the hardware speaker still marked as * (default).

Finding your hardware sink name

If your sink name is different, run wpctl status and look for your speaker under Sinks. Then run:

wpctl inspect

The node.name property is what you need for filter.smart.target.

Why the old guides don’t work

Older configs use node.target in playback.props. This property is deprecated in PipeWire 1.x and WirePlumber silently ignores it in most cases when the hardware is set as the default sink. filter.smart is the correct approach as of WirePlumber 0.5, and it’s documented in the official WirePlumber docs under [Smart Filters](Smart Filters — WirePlumber 0.5.13 documentation).

Tested on

  • Dell XPS 15 9530
  • Fedora 43
  • KDE Plasma 6.6.2
  • PipeWire 1.4.10
  • WirePlumber 0.5.x