please help this noob understand how Linux drivers work

Hello,

Within a larger diy project I am working on, I am trying to build a i2s to spdif and spdif switch (to select between this spdif signal and other external spdif signals) hat for a raspberry pi around the old wm8805 chip. Wm8805 is capable of i2s to spdif and has additional 8 spdif inputs that can be routed to an unique spdif output. Its very similar brother wm8804 does i2s to spdif but lacks the multi spdif inputs and the multiplexer circuit.

As you all know wm8804 is a sort of standard for raspberry pi i2s to spdif hats, and there are drivers for it in linux. For instance this generic driver:

https://github.com/torvalds/linux/blob/master/sound/soc/codecs/wm8804.c

The only hat for raspberry pi I know based on wm8805 that I am aware of is the allo digione, but this hat is just another i2s to spdif and lacks the additional spdif inputs and input selection capabilities. Its driver is based on the one for wm8804, to the point that the device calls itself wm8804 in the code. The driver and overlay can be found here:

https://isrc.iscas.ac.cn/gitlab/mir...f01428c736a5238a/sound/soc/bcm/allo-digione.c

https://github.com/raspberrypi/linu...rm/boot/dts/overlays/allo-digione-overlay.dts

I could not find any project using both i2s to spdif and spdif input selection capabilities of wm8805 for raspberry pi, even though they exist for Arduino, some even in this forum, like the very cool Whazon:

https://www.diyaudio.com/community/threads/whazon-a-s-pdif-switch-built-around-wm8805.306342/

an Arduino library to control wm8805 also exists here:

https://github.com/kabturek/WM8805


I am a complete noob in linux and in computers in general but with all this information, I believe it is possible to make wm8805 work as a i2s to spdif with a raspberry pi using the wm8804 codec (like allo digione does), but what about the additional functionality? If I read through the drivers I know of, and I don’t understand 10% of what is written there, I don’t see any indication that the additional features of wm8805 are covered….

My question is: do you think it is possible to have a wm8805 working with a raspberry pi as i2s to spdif card using the wm8804 driver AND at the same time controlling the spdif switch function through i2c (via a python script or whatever…) OR do I need to forget about the wm8804 driver and try to make it work like in the Arduino projects mentioned OR there is no hope, better waste my time somewhere else?

Thanks a lot for any comments, suggestions or directions!
 
allo digione...is just another i2s to spdif
It may be more than you realize. SPDIF carries both digital and analog information. The analog part the embedded clocking, which is technically analog. It subject phase noise and or jitter effects, which can then potentially affect dac sound quality. Digione has a shield can over the low phase noise NDK SDA clocks, and probably some local voltage regulation just for the clocks. The device also works best on very clean external power, which Allo also offers at a discount if acquired along with digione

Other than that, the linux local experts sometimes come around here but mostly they are over in the 'PC Based' sub-forum. RPi OS guys are there too. That said, there is an old expression in the computer business, 'RTFM, or Read the Manual. Historically, the experts were more likely to help people who did some homework first before asking for help. Don't know about now, though.

Anyway there is some documentation on the Alsa sound engine, which the one used used for serious audio.
Some possibly useful links to help get started:
https://alsa.opensrc.org/WritingAnAlsaDriver
https://alsa.opensrc.org/
 
Hello Mark,

Thank you for your answer. I would certainly read a manual if there was one I know of. My intention is not to write a new driver, my questions don’t go in that direction.

Regarding the Allo card, what I mean is that functionally it is just a i2s to spdif card, meaning that it does not make use, to the best of my knowledge, of the multiplexer capabilities of the wm8805.

Thanks for the suggestion on posting in the right forum, i am not very familiar with diyaudio. I will try to see if I can change it. Thanks.
 
@MRamone: I do not think there is an official ASoC codec driver for wm8805. There is a driver for wm8804 which if you compare the datasheets does not define the PLL6/bits 2:0 which in wm8805 mux the RX inputs. The linux wm8804 ASoC codec driver does not use these bits of the WM8804_PLL6 register. See WM8804_PLL6 usage in https://elixir.bootlin.com/linux/latest/A/ident/WM8804_PLL6 - only clk-related tasks are handled, as supported in wm8804. Therefore the alsa driver does not offer any alsactl mixer control for switching the RX inputs.

Theoretically you could send appropriate I2C commands directly to your wm8805 from userspace, operating the mux outside of the kernel driver. You can play with i2cset to find out.
 
  • Like
Reactions: 1 users
thank you pavel, this is what i was thinking the situation was: no specific driver for wm8805 and the one for wm8804 does not consider the multiplexer funtion.
Thanks for the confirmation that it might be possible to control the mux outside the driver, will dive in and see what i can do/test.
Much appreciated,
M.
 
@MRamone: If the driver does not lock the I2C address somehow to avoid conflicting commands to the device (which I do not think it does), the userspace approach should work. Just read the PLL6 register byte and update it with the required RX input bits. That should be quite straightforward, no need to hack the driver. The Alsa SoC framework is VERY complex, full of fragile macros, it seems a bit of a minefield to a barely occasional hacker (i.e myself).
 
  • Like
Reactions: 1 user
@ppp000: The ASoC framework separates the chain to individual standardized parts and layers - see e.g. https://docs.kernel.org/sound/soc/overview.html or more exhaustive https://elinux.org/images/b/b5/Belloni-alsa-asoc.pdf . The BCM2835 I2S driver is "just" for the platform part (I2S + DMA). This together with the codec driver needs to form the machine-class driver - e.g. this https://github.com/raspberrypi/linux/blob/rpi-6.2.y/sound/soc/bcm/rpi-wm8804-soundcard.c

Or the general https://www.kernel.org/doc/Documentation/devicetree/bindings/sound/simple-card.txt which allows configuring everything in DTS. For my project I use this DTS https://github.com/AkiyukiOkayasu/RaspberryPi_I2S_Slave which includes only the BCM2711 I2S DAI and ASoC codec driver for SPDIF which basically has no functionality (an empty codec driver) . Then my codec and clock chips are controlled by userspace I2C completely.

The very same approach can be used for WM8804/5, but then the userspace I2C code will have to do all the initialization/control. Most of it (maybe even all for wm8804) is already coded in the existing ASoC codec wm8804 driver. That's why IMO it would make sense to use wm8804 as is and just do the RX muxing from userspace.
 
  • Like
Reactions: 1 user
Yes, no neeed to reinvent the wheel.
And thats what I meant - use WM8804/5 overlay for i2s + write a script in python for example to control muliplexer via i2c. -easiest way i guess
Or modify existing overlay i2c part if you feel like you want to:)

Description of i2c registers you can find in documentation https://statics.cirrus.com/pubs/proDatasheet/WM8805_v4.5.pdf

Thats the most interesting part for you:
wolfson.png
 
  • Like
Reactions: 1 user
@phofman when you say, "while the driver does not lock i2c..."
Is the "UU" result when running i2cdetect an indication of that happening? Because if yes, i am afraid that the wm8804 driver does exactly that.
If that is the case, do you think i can still blacklist the driver and do everything from userspace?
Thanks for your comments!
 
i think i have found an alternative solution if using the driver AND communicating via i2c simultaneously does not work:

Actually, according to the datasheet, wm8804-5 can use two different i2c addresses, 0x3a (58) and 0x3b (59) depending on the status of the pin 9 (CSB) on boot:
Unbenannt.jpg

The kernel driver expects the pin to high and assumes 0x3b, while for instance the two arduino projects that i know of have pin 9 to ground and use the address 0x3a.

So if all other alternatives fail, i could have two chips: one wm8804 or 5 with pin 9 high (address 0x3b) doing the i2s to spdif tasks using the kernel driver and a second wm8805 with pin 9 low that will be in 0x3a and not locked by the kernel driver, doing the spdif selection tasks and controlled via python/i2c (SMBus2 for instance??)

Not very elegant but seems likely to work....
 
IMO there is no reason for the -f switch to not work. The I2C driver serializes transactions so no interwined transactions should occur.

I just re-read your requirement - do I understand correctly that output of your device would be SPDIF, and you want to mux between several incoming SPDIF lines and one SPDIF line from RPi? If so, a simple N:1 multiplexer controlled by I2C or any GPIO would do, no need for a full-featured SPDIF chip. In fact the I2S-> SPDIF could be just an SPDIF transmitter, no SPDIF receiver functionality required. IIUC.
 
  • Like
Reactions: 1 user
I just got scared when I read the link you posted about using i2cset with -f
"Using this flag is dangerous, it can seriously confuse the kernel driver in question. It can also cause i2cset to silently write to the wrong register. So use at your own risk and only if you know what you're doing."
And certainly i can't say i know what i am doing :D
Yes, i evaluated the possibility of just using a mux chip and i believe as well it would do, but with the wm8805 you can make it work as signal sense switches (automatically detects and locks incoming spdifs signals) and even though it is not my intention to go directly for this (i will be more than happy to make it work manually) i want to have that door open in the future, as it is just a matter of coding...
But thanks again for your comment, makes me more confident with using i2cset with force.
 
Well generally it's not nice when one device is being controlled by multiple masters. A driver may assume that what it wrote into a register is there and avoid proper atomic read/modify/write sequence. But the wm8804 driver uses the standard atomic read/modify/write method for writing to PLL6 (the ASoC method snd_soc_component_update_bits does so internally, if you analyze it in https://elixir.bootlin.com/linux/latest/source/sound/soc/soc-component.c#L788 ) and seems to be safe for your purpose.

The TXSRC bit of wm8804/5 seems to be operated by the alsa ctl "Input source" https://elixir.bootlin.com/linux/latest/source/sound/soc/codecs/wm8804.c#L100 so your plan with single wm8805 looks viable and pretty clever for your use case.
 
  • Like
Reactions: 1 user