Repository navigation
Tezro: the ICE1724 audio controller - #640
Open
jamesbraid wants to merge 3 commits into
Open
jamesbraid wants to merge 3 commits into
jamesbraid wants to merge 3 commits into
Conversation
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 07:17
752ff03 to
2804975
Compare
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 09:06
2804975 to
dff3386
Compare
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 09:12
dff3386 to
20161a9
Compare
jamesbraid
marked this pull request as ready for review
November 12, 2026 09:37
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 09:44
20161a9 to
42b5b1a
Compare
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 14:45
42b5b1a to
86d28b6
Compare
A type 1 header modelled only the 16-bit I/O window: its I/O base and limit registers read 0 in the capability nibble and the upper 16 bits at 0x30 were fixed at zero. A bridge window above 64 KiB could not be programmed, so no I/O BAR placed there forwarded. A host bridge whose PCI I/O windows start on megabyte boundaries, as the Origin and Tezro PIC's do, puts every I/O BAR above 64 KiB, and a function behind a bridge on such a machine was unreachable: the guest's accesses ended in master abort. The Tezro's onboard audio chip sits behind the TI bridge and hit exactly this. Report 32-bit I/O addressing in bits 3:0 of the I/O base and limit, as the TI PCI2050 does and as the PCI-to-PCI bridge specification allows, store the upper halves, and forward an I/O BAR that lies wholly inside the window they make. A window with zero upper halves is the old 16-bit one. The TI PCI2050 also stores the upper 32 bits of the prefetchable base and limit, which stay read-only zero in other bridges. Only the Tezro's bridge takes this, through a new constructor (`PciToPciBridge::with_wide_windows`): the registers are established for the PCI2050 and not for the other bridges the machines fit, whose behaviour does not change. Nothing calls the constructor until a device is fitted behind that bridge; tests cover the window registers and the forwarding.
The Tezro lists an IC Ensemble ICE1724 (Envy24HT, 1412:1724) as its audio controller, and the DMediaPro DM8 option card is the same chip. Nothing in iris could answer to it, so a guest that probed for it found no audio: hinv had no audio processor and sfplay had nothing to open. Model the controller as a PCI function: a byte-granular register file behind each of its two I/O BARs, seven DMA engines sharing one sample rate, the configuration EEPROM on the I2C port, the two write-only converters behind bit-banged GPIO pins, and an AC'97 variant without either. The interleaved engine plays all its slots, and the S/PDIF engine mixes into the front pair, through one host stream with as many channels as the host device has; a narrower host gets a standard downmix. Recording writes silence, as iris has no host capture path. A host with fewer speakers than slots gets the ITU-R BS.775 stereo matrix with nothing scaled down: each speaker plays its own channel at unity, centre and surrounds fold in at -3 dB, the low-frequency channel is dropped, and a sum past full scale saturates. Scaling each speaker's folds to sum to unity would keep a full-scale 7.1 frame from clipping, but IRIX plays stereo in the first two of eight slots with the rest silent, and that scaling played every IRIX sound 9.9 dB quiet (0.32 of the file's level). This is a choice, not the chip's behaviour. So is the fold BS.775 has no rule for: on a 5.1 or quad host the side pair folds into the back pair at unity. A mono host falls through to the stereo layout, unchanged. Which speaker each slot drives is not established for this card either: the usual host order (front pair, centre, low frequency, back pair, side pair) is inferred. The header uses the bus's policy for read-only and write-one-to-clear bits, and the function answers only what the chip defines beyond that: the power-management state, the subsystem pair behind its write enable, and the interrupt status bit. The reset state masks every interrupt source, and interrupts follow the OR of the unmasked sources the vendor's documentation gives. Accesses and values it does not establish are logged under the new dm8 log module and counted. D3hot to D0 resets the registers but not the header, because a function cannot reset its own header without leaving the bus's decode table stale; the module notes list that and the other approximations. A transfer to or from guest memory runs with the state lock let go, so the bus and memory locks below the card are never taken inside it, and a flag keeps other paths that move the engines out until it ends. IRIX steers its playback by polling. It masks the engine's interrupt, reads the address register about once a millisecond and writes only about 2 ms past the position it read. The card's clock is host time, so when the host does not run the guest for longer than that, an engine that reads on passes what was written and plays the previous lap of the 23 ms ring again: a buzz on a loaded host. In IRIX 6.5 runs on a host at load 400 to 650, 4 to 19% of the samples played were a lap old, and the captured output repeated exactly one ring at a time. So, as an emulator policy with no hardware counterpart, the one HAL2 already uses for IRIX, an engine whose interrupt is masked and whose address or count the guest read in the last 50 ms moves no more than 1.5 ms past the position read, and waits there; time held beyond 100 ms is dropped. A run with it replayed 0.2%. HAL2 waits at 1 ms; here 1 ms held the engine below real time whenever a poll came late and put short gaps in the output. IRIX's polls were 1.00 ms apart at the median, p90 1.03 to 1.07 ms and p99 1.1 to 2.8 ms on a loaded Linux host (p99 up to 13 ms on a loaded Mac), so 1.5 ms sits above most poll gaps and below the 2 ms IRIX writes ahead. A guest the host holds up still loses the time, as a gap instead of an old lap: on a heavily loaded Mac, gaps of 1 to 500 ms in the output went from 46 (285 ms) to 185 (894 ms), so there the buzz turns into stutter. The polling and the 2 ms are measured in IRIX runs, not documented, and only IRIX was measured. An engine that takes its interrupt, or whose position nobody reads, is not held. The risk is a guest that masks the interrupt, polls further apart than 1.5 ms and writes a whole ring ahead: it is held to 1.5 ms of audio per poll, so 10 ms polls would play at about 15% of real time, with gaps. No such guest is known to run. Cost of the read-ahead: a read of an engine's address or count register stores the time and frame count the guest saw (a compare on the offset and one store under the lock the read already holds), and each engine sync takes one extra branch on that record. Both are in this device's own code, which exists only on a Tezro with audio switched on, so no other device and no CPU path pays for it. It was not timed; on arm64 the stores are plain ones, and the instruction count is a handful per register read. Without a host audio device in the build container, whether the output is audible and what the samples sound like are unverified. The subsystem ID is the DM8 board's recipe, a model choice: the Tezro board's own is not known. Tests cover the register contract and access widths, the engines' timing against a fake clock, the read-ahead against a modelled IRIX writer, the sink, that no transfer holds the state lock, and the card on a real bus behind a PCI-to-PCI bridge.
Tezro hardware listings print an ICE1724 audio controller at device 2, beside the TI PCI-to-PCI bridge, and the bridge was the one device on the IO9 with an empty secondary bus. By exclusion the controller sits at device 2 of that bus. With it fitted, IRIX attaches it: hinv lists an Iris Audio Processor (MAD revision 1), /hw/audio/MAD1 appears, and sfplay moves frames. Add [tezro] audio, off by default so the empty bus stays the safe behaviour. When set, the controller is fitted at device 2 of the bridge's secondary bus, started and stopped with the board, given the machine's timers, and played to the host as [audio] says, or silently under no_audio. It takes the DM8 board's converters, subsystem pair and configuration EEPROM, the one design with a documented recipe; the Tezro board's own are unknown. The reset state masks every interrupt source, so nothing reaches the PIC unless a guest unmasks one. The monitor lists the controller and prints what it has done: the engines' frame counts, the converter writes and the count of accesses nothing establishes. Without a host audio device in a build container, that is the only way to tell a guest that plays from one that does not. With no_audio set the card is still fitted, so the guest sees an audio processor and plays to it, and nothing comes out: no stream is opened and nothing said why. Someone who sets both keys sees silence and has no clue. Say so twice: iris prints one line at startup when it fits the card with no_audio set, and --validate warns about the same combination. Both use one message. A function at device 2 behind the bridge raises INTC of the bridge's slot once the bridge's pin rotation has carried it, and the PIC routed only INTA, so attaching it panicked the IRIX guest on an unrouted pin. INTC of device d now goes to the same input as its INTA, d - 1, as IRIX's pin rule gives. No function on the IO9 raises INTC itself, so the machines without the card are unchanged; INTB and INTD stay unwired because nothing establishes them. A PIC test with a function behind a bridge at device 2 fails without it. Tests place the controller on the board and read its identity, class, subsystem pair and interrupt pin through the bridge's bus, check the bus stays empty without the key, and check the status text. The CHANGELOG gets the one bullet: the key is new and a user sets it.
jamesbraid
force-pushed
the
audio/ice1724
branch
from
November 12, 2026 14:49
86d28b6 to
4adb500
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Tezro lists an ICE1724 audio controller behind the TI bridge on its IO9, and iris had nothing to answer to it, so IRIX had no audio processor and sfplay had nothing to open. This adds the controller and fits it with
[tezro] audio = true(off by default).Three commits:
pci: give a PCI-to-PCI bridge 32-bit I/O windows: the Tezro's PIC puts I/O BARs above 64 KiB, which a 16-bit window cannot forward, so a function behind the bridge ended in master abort. Only the Tezro's bridge takes it, throughwith_wide_windows.dm8: add the ICE1724 PCI audio controller: seven DMA engines, EEPROM, converters, AC'97 variant, multichannel output with an ITU-R BS.775 downmix, and a read-ahead hold-up for IRIX's polling (measured in IRIX runs on a loaded host: 4 to 19% of samples a lap old without it, 0.2% with it).ip53: fit the ICE1724 audio controller behind the TI bridge on request: the machine wiring, theno_audiowarning, the monitor listing, the CHANGELOG bullet, and PIC INTC routing, which the card at device 2 needs (without it the IRIX guest panicked on an unrouted pin; a PIC test fails without it).Scrutinise: the hold-up approximation is an emulator policy, not the chip's behaviour; the downmix is a choice; the subsystem ID is the DM8 board's recipe. Cost of the read-ahead is in the dm8 commit: inside this device only, not timed, arm64 unmeasured.
Held for a listening test on a real output: nobody has heard it, because the build container has no audio device. The IRIX guest plays, sfplay moves frames and the frame counts are visible in the monitor.
Depends on the PR below for the shared sink and the multichannel open.