diff --git a/NEWS b/NEWS
index e4a6668b94..da610a6ac7 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ ver 0.25 (not yet released)
- wasapi: detect device changes
* mixer
- fix mixer idle events on non-default partitions
+ - add pipewire config sink_volume "yes"
* tags
- new tag "DiscSubtitle"
* player
diff --git a/doc/plugins.rst b/doc/plugins.rst
index 528815186b..cc52ad82f5 100644
--- a/doc/plugins.rst
+++ b/doc/plugins.rst
@@ -1216,6 +1216,9 @@ Connect to a `PipeWire `_ server. Requires
The default is ``yes``.
* - **dsd yes|no**
- Enable DSD playback. This requires PipeWire 0.38.
+ * - **sink_volume yes|no**
+ - Control the sink's volume instead of the stream's per-client
+ gain. The default is ``no``.
.. _pulse_plugin:
diff --git a/src/mixer/plugins/PwSinkMixerPlugin.cxx b/src/mixer/plugins/PwSinkMixerPlugin.cxx
new file mode 100644
index 0000000000..036bf7a710
--- /dev/null
+++ b/src/mixer/plugins/PwSinkMixerPlugin.cxx
@@ -0,0 +1,953 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright The Music Player Daemon Project
+
+/*
+ * "pwsink" mixer plugin for the PipeWire output.
+ *
+ * By default, the "pipewire" ao's mixer controls MPD's own per-client
+ * stream volume. This plugin instead controls the sink volume itself,
+ * via a second, independent PipeWire connection.
+ *
+ * Volume is scaled cubically to match `wpctl` (set "mixer_volume_curve"
+ * to "linear" to disable).
+ */
+
+#include "PwSinkMixerPlugin.hxx"
+#include "mixer/Mixer.hxx"
+#include "config/Block.hxx"
+#include "lib/fmt/RuntimeError.hxx"
+#include "output/plugins/PipeWireOutputPlugin.hxx"
+#include "Log.hxx"
+#include "util/Domain.hxx"
+#include "lib/fmt/ExceptionFormatter.hxx"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include