Verify at the board: S/PDIF output (PH17 card detect: done) #42
Labels
No labels
blocked-physical
cleanup
hardware
infra
kernel
P1-critical
P2-high
P3-normal
P4-later
reliability
security
upstream
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tiagoagueda/a80#42
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Two things that need someone physically at the board. Both are quick.
1. Does a bitstream actually leave PH18?
S/PDIF now registers a card and plays — DMA interrupts advance,
pll-audioreparents to122.88 MHz and the module clock lands on exactly 24.576 MHz. But nothing has confirmed a
signal on the pin, and it is not even confirmed the Telos fits the connector: "HDMI, AV,
SPDIF" in 01-hardware.md is [REPORTED], from a WikiDevi teardown of the
Meta variant.
Needs an S/PDIF receiver on the optical/coax jack, or a scope on PH18:
Note PH18 function 3 is "Reserved" in the public A80 manual — which documents no S/PDIF block
at all — so the pinmux claim rests on the board's own
script.binand the vendor kernel.2. Does PH17 track card presence?
The card detect pin was PH18, taken from the
sys_config.fexin the vendor rootfs — thefile that claims
machine = "cubieboard4"and that 33-REAL-PINMAP.mdalready flagged as wrong about USB, WiFi and the A15 rail. The board's own script.bin has
sdc_det = port:PH17.This retroactively explains a note that sat in the board DTS: with a pull-up on PH18 the line
never went low on insertion, and the conclusion recorded was "the detect switch is not
reaching PH18". It is not — it is on PH17.
broken-cdis kept for now because polling is known to work. Insert and remove a card whilewatching PH17; if it tracks,
cd-gpioscan replace the polling.Update 2026-08-28 - part 1 got worse before it got better, and is still open.
S/PDIF was running at the wrong rate the whole time.
pll-audio's M divider was modelled witha zero offset where the hardware divides by M + 1, so the module clock that this issue records
as landing "on exactly 24.576 MHz" was really 23.6 MHz. It read back as exact because the
framework reports what it asked for.
Measured by timing a buffer of known length on the S/PDIF card:
So anything played at the pin before today was 4% flat, and nobody could have heard that
without a receiver - which is exactly what this issue is for. Fixed now, but it means the
"clocks verified" claim behind the original S/PDIF result was worth less than it looked.
Also inherited: the 44.1 kHz family is still 9.5% slow on this same PLL, #52. Any bitstream
check at 44.1 kHz will be measuring that bug, so test at 48 kHz.
Neither half of this issue is resolved - still nothing has confirmed a signal on PH18, and the
card-detect test on PH17 is still untouched.
Part 2 (PH17 card detect) is done — only S/PDIF remains
cd-gpiosreplaced thebroken-cdpolling in commit97a6d25, and today it was exercised properly rather than just configured:sunxi-mmc 1c0f000.mmc: Got CD GPIOSo PH17 tracks presence, and
sys_config.fex's PH18 claim is confirmed wrong for a third subsystem after USB, WiFi and the A15 rail.Still open, and still
blocked-physical: part 1, whether a bitstream actually leaves PH18. That needs an S/PDIF receiver on the jack or a scope on the pin, and it also needs someone to confirm the Telos even fits the connector — "HDMI, AV, SPDIF" in01-hardware.mdis still[REPORTED], from a teardown of the Meta variant.Verify at the board: S/PDIF output, and SD card detect on PH17to Verify at the board: S/PDIF output (PH17 card detect: done)Verified at the board, and it was not working
This issue was
blocked-physicalwaiting for someone to listen to the connector. Doing thatfound a driver bug that no register on the board reveals.
S/PDIF output now works. Kernel
7.2.0-14859-g613f14be7ca6, deployed and promoted to known-good.What was wrong
The A80 TX FIFO consumes one 32-bit word per IEC 958 subframe, and takes the sample from the
top of that word.
sun4i-spdifadvertises four formats and mapsS24_LEandS32_LEto thesame
FMT24BITas though interchangeable. Only one of the four lines up:S16_LE(16-bit DMA, as upstream)S16_LE(32-bit DMA)S20_3LE,S24_LES32_LEMeasured, which is what made the double-speed case provable:
The transmitter is correct in both cases - 88200 subframes/s is exactly 44100 stereo frames.
It is the buffer that drains at the wrong rate.
Why it needed ears
⚠️
TXCFG,FCTL,FSTAand the channel status bytes read identically for the correct,silent and double-speed cases. No error bit, no underrun, zero xruns. A register diff between a
working and a broken configuration returns nothing.
⚠️ A sine wave cannot detect this.
speaker-testproduced an audible tone, and that wasbriefly taken as proof the path worked. It is not - a mangled sine still sounds like a tone at
roughly the right pitch. Only music exposes it, and the first real signal was hearing "pulsed
gibberish" instead of a clean track.
Evidence for closure
pin 242 (PH18): 6001000.spdif (GPIO UNCLAIMED) function spdif group PH18state: RUNNING,rate: 44100 (44100/1), 0 xruns,TXCNTadvancing at 88200subframes/s
closedthroughout, so audio left via PH18 and nowhere elseand then eliminated
Fix
patches/linux-asoc-sun9i-spdif/0002-*.patch- a quirk describing the FIFO behaviour and a maskremoving the formats it cannot carry, so userspace converts to
S32_LE.plughw:1,0on anordinary 16-bit file plays correctly;
hw:1,0with S16 now fails honestly with "Sample formatnon available" rather than playing at double speed.
Worth noting for anyone touching this: the mask is applied to
sun4i_spdif_dai, because that iswhat is passed to
devm_snd_soc_register_component().host->cpu_dai_drvismemcpy'd andrenamed in probe and then never registered - so the obvious place to put this change has no
effect at all. Arguably a second, latent bug.
This is an upstream candidate: it affects any sun9i board, and the quirk is small. Full write-up
in
59-spdif-s32-only.md, including two wrong turns I made on the way, both of which lookedcorrect from the measurements.
Not covered by this issue
AC-3 / DTS passthrough remains untested. IEC 61937 frames the compressed stream as 16-bit
stereo - exactly what this FIFO cannot carry - so it needs the payload pre-expanded to 32-bit
words. It also needs the IEC 958 non-audio bit, and
amixer csetonIEC958 Playback Defaultdoes not stick (reads back
AES0=0x02regardless), so the driver never setsTXCFGbit 16.Without that flag a failure is indistinguishable from noise. Worth its own issue if passthrough
is wanted.
Correction: the root cause above is wrong
Leaving the previous comment in place rather than editing it, because the wrong turns are part of
the record - but do not act on it. It named the format alignment as the cause and the fix as a
format mask. Both were symptoms.
The actual cause is one bit.
sun4i_spdif_configure()programsFCTL.TXIMonce per device:That bit selects where the FIFO takes valid data from, and the correct setting depends on the
sample width, not the device. A 16-bit sample written by a 16-bit DMA access sits in the low
half of the word; a 32-bit one fills it. Programmed once it can only ever suit one width:
TXIMclear (mainline)TXIMset (vendor)S16_LES24_LES32_LENow set in
hw_paramsalongside the format, under a quirk. All formats work. Verified in bothdirections - each is reproducibly broken with
TXIMpinned the wrong way.Kernel
7.2.0-14859-gc545046f1f86, deployed and promoted to known-good. Patch rewritten aspatches/linux-asoc-sun9i-spdif/0002-*.patch; the earlier format-mask patch is gone, not keptalongside. Write-up in
59-spdif-txim.md(renamed - the old title asserted "only S32 works", whichis no longer true).
This is a genuine mainline bug for sun9i, not a board quirk, which makes the upstream case
stronger than when this issue was closed.
How it was found, since it generalises
Four hypotheses died on the way - widening the DMA, dropping S16, dropping S24, and setting
TXIMglobally. Every one was killed by the listener, not by an instrument, because every register on
this block reads identically for a stream that is music, noise or silence: no error bit, no
underrun, zero xruns. A register diff between working and broken returns nothing.
And a sine cannot find it.
speaker-testproduced an audible tone through a badly brokenconfiguration and that was briefly taken as proof the path worked - a mangled sine still sounds
like a tone at roughly the right pitch.
The answer came from the vendor sun9i driver in the CC-A80 tree archived under #56 while
hunting the PowerVR DDK, not from searching the web. That archive has now paid for itself twice in
one day.
Two traps recorded in the note for whoever is here next:
amixer csetsilently writes the wrongbytes to an IEC958 control (use
iecset), and reading these registers while the block isclock-gated returns
0x2for every one of them.