Some time ago, one of our customers came to us with a hardware design featuring the Renesas X9250 digital potentiometer in an audio path, as shown in the following diagram. Naturally, our goal was to find the proper way to support this hardware design in the upstream Linux kernel.

Since the X9250 is part of an audio path, the first thing that came to mind was the ALSA subsystem of the Linux kernel. However, the Renesas X9250 is a generic digital potentiometer, and audio is only one of its many possible use cases.
The Linux kernel also has a separate subsystem called IIO (Industrial I/O), which provides support for a wide range of devices, including accelerometers, various types of sensors, ADCs, DACs, and potentiometers. Potentiometers are already supported by IIO, as can be seen in the drivers/iio/potentiometer directory.
IIO is not specific to audio, so implementing the Renesas X9250 driver as an IIO driver made more sense than implementing an ALSA-specific driver. This way, the same driver can be reused for non-audio use cases as well.
However, we still needed a way for the potentiometer to be handled as a regular component of an audio path through the ALSA subsystem.
Enter audio-iio-aux
To provide a bridge between the ALSA and IIO worlds, we developed and upstreamed the audio-iio-aux driver.
This driver exposes auxiliary audio devices. In ALSA terminology, these are audio devices that do not provide a Digital Audio Interface (DAI): they do not consume or produce digital audio themselves. Instead, they are typically present in the analog part of an audio path, for example as analog amplifiers or filters.
On one side, audio-iio-aux consumes an IIO device, such as a potentiometer. On the other side, it registers an auxiliary audio device with the ALSA subsystem.
The auxiliary audio device exposes ALSA mixer controls. When these controls are adjusted, audio-iio-aux translates the changes into requests to the underlying IIO device and its driver.
Using audio-iio-aux
Once enabled using CONFIG_SND_SOC_AUDIO_IIO_AUX, the driver can be configured through its Device Tree binding.
As an example is worth a thousand words, let’s look at a complete Device Tree example:
spi: spi@a80 {
...
pot_in: x9250@1 {
compatible = "renesas,x9250u";
spi-max-frequency = <1000000>;
reg = <1>;
vcc-supply = <®_5v>;
avp-supply = <®_p5va>;
avn-supply = <®_n5va>;
spi-cs-high;
#io-channel-cells = <1>;
};
pot_out: x9250@5 {
compatible = "renesas,x9250u";
spi-max-frequency = <1000000>;
reg = <5>;
vcc-supply = <®_5v>;
avp-supply = <®_p5va>;
avn-supply = <®_n5va>;
spi-cs-high;
#io-channel-cells = <1>;
};
};
sound {
compatible = "simple-audio-card";
simple-audio-card,aux-devs = <&_in>, <&_out>;
simple-audio-card,routing =
"CODEC LEFTIN", "AMP_IN LEFT OUT",
"CODEC RIGHTIN", "AMP_IN RIGHT OUT",
"AMP_OUT LEFT IN", "CODEC LEFTOUT",
"AMP_OUT RIGHT IN", "CODEC RIGHTOUT";
simple-audio-card,additional-devs {
amp_out: iio-aux-out {
compatible = "audio-iio-aux";
io-channels = <&pot_out 0>, <&pot_out 1>;
io-channel-names = "LEFT", "RIGHT";
snd-control-invert-range = <1 1>;
sound-name-prefix = "AMP_OUT";
};
amp_in: iio-aux-in {
compatible = "audio-iio-aux";
io-channels = <&pot_in 0>, <&pot_in 1>;
io-channel-names = "LEFT", "RIGHT";
sound-name-prefix = "AMP_IN";
};
};
simple-audio-card,cpu {
sound-dai = <&cpu>;
};
simple-audio-card,codec {
sound-dai = <&codec>;
clocks = <&clocks>;
};
};
So, in detail:
- The Renesas X9250 potentiometers are first described as SPI devices in the Device Tree nodes named
x9250@1andx9250@5. We have two potentiometers on the same SPI bus, each with its own chip select. The first has the Device Tree labelpot_in, while the second has the labelpot_out. The driver forrenesas,x9250uwill register an IIO device for each of these nodes. - The
soundnode then describes the whole audio card, using the popular simple-audio-card Device Tree binding. The example uses the usualsimple-audio-card,cpuandsimple-audio-card,codecnodes to describe the CPU-side and codec-side DAIs. - As part of our work on audio-iio-aux, we introduced the new
simple-audio-card,additional-devschild node ofsimple-audio-card. Here, we instantiate two devices,iio-aux-outandiio-aux-in. Using theirio-channelsproperties, these devices referencepot_outandpot_in, respectively, which are the IIO devices described above. audio-iio-aux then instantiates two auxiliary audio devices. These are referenced by thesimple-audio-card,aux-devsproperty and subsequently used in thesimple-audio-card,routingaudio routing table. TheAMP_IN LEFT OUT,AMP_IN RIGHT OUT,AMP_OUT LEFT IN, andAMP_OUT RIGHT INaudio components therefore become available from the audio routing point of view thanks to audio-iio-aux.
With all this Device Tree wiring in place, the potentiometers become part of the audio path and can be controlled using amixer and/or alsamixer.
Conclusion
The audio-iio-aux driver is available upstream. It was contributed in 2023 as part of [PATCH v6 12/13] ASoC: codecs: Add support for the generic IIO auxiliary devices and landed in Linux v6.6.
Bridges between kernel subsystems such as this one allow end users to control their hardware using the subsystem that is most appropriate for each device, without requiring duplicate drivers or subsystem-specific implementations.
In this case, the X9250 can remain a generic IIO device, while audio-iio-aux provides the glue needed to integrate it into an ALSA/ASoC audio card. No additional driver code is required: the integration is entirely described through Device Tree.
This small driver is therefore a good example of how connecting existing kernel subsystems can make hardware support both more reusable and easier to integrate.
