# iMac19,2 internal mic: silent capture despite maxed gain ## TL;DR The internal mic shows up normally in PipeWire/ALSA/Discord, with fully adjustable gain controls, but captures almost nothing regardless of how high any gain is pushed. Root cause: the CS8409 codec driver's HDA auto-config binds the exposed capture PCM stream to the wrong ADC node, orphaning the real internal-mic ADC entirely — its mixer controls work, but nothing is ever routed through them. A fix for this exact bug, confirmed on a sibling Mac model, is packaged into this repo's build (`patches/snd-hda-codec-cs8409/`) but **not yet verified on this exact iMac19,2 hardware** — see [Current verification status](#current-verification-status). ## Hardware / software identification - Model: Apple iMac19,2 (2019, 21.5") - Distro: Aurora (uBlue/Fedora Kinoite bootc image), Kinoite variant `44.20260908.1` - Kernel: `7.1.8-200.fc44.x86_64` - Audio codec: Cirrus Logic CS8409, HDA vendor id `0x10138409`, subsystem id `0x106b0f00` (confirmed via `/proc/asound/card0/codec#0`) - Secure Boot: unsupported/disabled on this hardware (`mokutil --sb-state`) — a documented non-goal of this fix; see [Open risks](#open-risks--unknowns). ## Symptom Mic appears as a normal PipeWire source (`Built-in Audio Analog Stereo`) and ALSA capture device (`CS8409 Analog`, card 0). All of the following were confirmed at 100% (or higher) with no effect on captured level: - ALSA `Internal Mic` capture volume: 100% (63/63), unmuted - ALSA `Internal Mic Boost`: 100% (2/2) - PipeWire source software volume: tested at 1.0, 4.0, and 8.0 (up to +18dB) - Discord's own in-app input-volume slider: maxed Direct measurement, bypassing Discord and any GUI entirely, via `pw-record --target ` / `arecord`, analyzed with `ffmpeg -af volumedetect`: | Gain setting | mean_volume | max_volume | |---|---|---| | PipeWire source @ 1.0x (100%, all ALSA gain also maxed) | -80.8 dB | -71.2 dB | | PipeWire source @ 4.0x | -59.7 dB | -50.3 dB | | PipeWire source @ 8.0x | -59.7 dB | -50.3 dB (uncontrolled re-test, not a true ceiling — see note below) | The 1x → 4x jump is real, reproducible signal (matches the expected amplification). The identical 4x/8x reading is *not* evidence of a hard ceiling — it came from two takes that weren't spoken into with controlled, identical volume/distance, so it's not a clean A/B. Even at its best measured point (-50dB max), this is far too quiet for usable speech (normal speech should peak around -12 to -6dB) — consistent with "gain is working, but there's very little real signal to amplify in the first place." ## Ruled out, with evidence - **Wrong/muted output device** — a real but separate issue found early on: the *output* sink was muted at 0% (unrelated to the mic). Fixed independently (`wpctl set-mute @DEFAULT_AUDIO_SINK@ 0`). - **PipeWire/ALSA mute or wrong default device** — `wpctl status`, `pactl list sources`, and `amixer` all independently agreed the mic source was unmuted and at 100%+ from three different vantage points. - **Discord-side gain/config** — Discord's own input-volume slider was already at maximum; its `settings.json` doesn't store per-account voice/mic settings in plaintext (those sync via Discord's own account storage), so this was ruled out by the maxed-slider observation rather than a config file. - **The camera providing the audio instead of the codec** — `arecord -l` shows exactly **one** ALSA capture device (`CS8409 Analog`, card 0); the FaceTime HD Camera (USB `05ac:8511`) exposes no audio interface to Linux at all (checked via `lsusb` + absence from `arecord -l`/`arecord -L`). - **Apple T2 chip audio routing** — ruled out: no Apple PCI bridge/T2 device present (`lspci -nn`), and iMac19,2 (2019 21.5") predates T2's presence in Macs' internal-mic path regardless. - **Missing Flatpak/portal permission for Discord's mic access** — `wpctl status` showed Discord's own `WEBRTC VoiceEngine` client (verified by cross-referencing the sandboxed PID against the host process list) with an *active* capture stream connected to the real hardware node — a permission denial would show no connected stream at all, not merely low level. ## Root cause Traced to a specific, very recent (2026-08-19) upstream bug report and fix on a sibling Mac model: - **Issue**: [`egorenar/snd-hda-codec-cs8409` #5, "mic input"](https://github.com/egorenar/snd-hda-codec-cs8409/issues/5) - **Diagnostic dead-end + panic warning + D3 discovery** (2026-08-17): https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5314393975 - **Root cause + fix, confirmed working** (2026-08-19): https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5338459155 - **Fork/PR carrying Linux 6.17+ HDA API compatibility**: [PR #30](https://github.com/egorenar/snd-hda-codec-cs8409/pull/30), `daviddoll/snd-hda-codec-cs8409@kernel-6.17-hda-compat`, open/unmerged. In `patch_cirrus_apple.h`, function `cs_8409_apple_boot_init()`, the `SNDRV_PCM_STREAM_CAPTURE` branch only handles two cases for the capture stream's `hinfo->nid`: - `hinfo->nid == spec->intmike_adc_nid` — the real internal-mic ADC, set up correctly. - `hinfo->nid == 0x1a` — the headset-mic node, set up correctly. There's no fallback for when HDA auto-config lands the exposed capture PCM stream on **neither** — e.g. landing on node `0x12`, wired to an unconfigured external Line-In pin (`0x34`) with no physical signal path at all. When this happens, the real internal-mic ADC (`spec->intmike_adc_nid`, `0x23` for our subsystem-id group) has all its mixer/gain/switch controls correctly present and adjustable — which is exactly why raising every gain control had no effect — but is never attached to any PCM stream, so it's completely orphaned. Confirmed on the sibling model via `journalctl -kf` during a live `arecord` test: the internal-mic-specific prepare hook was never called, only the generic default path ran. Our subsystem id (`0x106b0f00`) is grouped together with the sibling model's family (`0x106b1000`, `0x106b0f00`, `0x106b0e00`) in this same file's `intmike_adc_nid` assignment logic — all three get `intmike_nid = 0x45`, `intmike_adc_nid = 0x23` — matching our own kernel log (`snd_hda_codec_cs8409 hdaudioC0D0: ... Internal Mic=0x45`). So this is a plausible direct match, though **confirmed only on iMac18,1, not this exact iMac19,2** — see [Open risks](#open-risks--unknowns). On the sibling model, applying the fix took capture from -91dB pure digital silence to a real -16.9dB mean / -0.0dB max signal. ## The fix See [`patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch`](../patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch) for the actual diff (source of truth — not duplicated here). In short: if the capture stream's `hinfo->nid` matches neither the internal-mic ADC nor the headset-mic node, force it onto the internal-mic ADC before the existing setup code runs. ### A second, related issue (defensive, unconfirmed on this model) On the sibling model, the internal-mic ADC node also came up in HDA power state D3 (suspended) after cold boot, failing capture with an I/O error until forced to D0 via `hda-verb ... 0x23 SET_POWER_STATE 0x0` — not persistent across reboots. This repo includes a defensive systemd unit (`cs8409-intmike-power-fixup.service`, running `overlay/usr/libexec/quine-linux/cs8409-intmike-power-fixup.sh`) that logs the power state and retries forcing D0 a few seconds after boot. **Whether iMac19,2 actually hits this at all is unconfirmed** — its own journal output after the real test boot is the evidence that will settle it. ## Packaging approach The fix is built into this repo's own bootc image rather than hand-patched live on the running system, specifically to avoid a documented panic risk (see [Open risks](#open-risks--unknowns)) and to get free, cheap rollback via `bootc rollback`/`rpm-ostree rollback` if anything goes wrong. - `patches/snd-hda-codec-cs8409/vendor/` — a verbatim copy of the 16 files from the pinned upstream commit (`daviddoll/snd-hda-codec-cs8409` @ `14e00de4290e090afd7632bc6d5c724131a19e7b`), vendored rather than fetched at build time because this is an unmerged personal-fork branch that could be force-pushed or deleted with no notice. See [`PROVENANCE.md`](../patches/snd-hda-codec-cs8409/PROVENANCE.md) for the full origin/license/rationale writeup (license: `GPL-2.0-or-later`, one file `GPL-2.0-only`, per each file's own original SPDX header). - `patches/snd-hda-codec-cs8409/0001-imac19-2-force-intmike-adc-nid-fallback.patch` — the actual functional change, kept separate from the vendored copy so it's easy to re-diff if/when PR #30 lands upstream and this vendoring can be dropped. - `Containerfile` — a new `kmod-builder` stage installs kernel headers matching the exact kernel shipped in the `aurora:stable` base image (pinned by digest, shared between both stages via a single `ARG`, so they can't drift apart), applies the patch, and builds the module with the vendored `Makefile`. The final stage copies the resulting `.ko` into `/usr/lib/modules//extra/` (the same convention Fedora's own `kmod-*` subpackages use to override an in-tree module of the same name/modalias — no blacklisting or renaming needed) and re-runs `depmod` against the final image's own module tree. - DKMS was deliberately **not** used: it exists to rebuild modules at boot/install time when a kernel updates later on a mutable host. On this bootc image, a new kernel only ever arrives bundled inside a freshly built image, at which point the `kmod-builder` stage reruns from scratch anyway — DKMS's rebuild-on-boot machinery is redundant weight here. ## Current verification status **Not yet tested on real hardware as of 2026-09-26.** The patch has been verified to apply cleanly (`git apply --check`) and produce the intended diff against the vendored source. The Containerfile has been written and a local `podman build` sanity check (module compiles, `vermagic` matches the target kernel) is the next step. Actually switching this machine to boot the built image (`bootc switch`/`rpm-ostree rebase` + reboot) is a separate, explicitly confirmed step, not yet taken, given this hardware/kernel/fix combination is entirely unverified. This section will be updated with real results (measured dB, Discord input-meter behavior, and whether the D3 power-state unit actually fired) once that test happens. ## Open risks / unknowns - **Kernel API mismatch risk**: the vendored fork targets Linux 6.17+ HDA API; our kernel is 7.1.8. Untested whether it compiles cleanly without further porting — but this fails safely at `podman build` time, before ever touching the running system. - **Unconfirmed on this exact model**: the fix is confirmed only on iMac18,1, not this iMac19,2/subsystem-id combination, even though the `intmike_adc_nid` code path is shared across both per direct source inspection. - **D3 power-state issue unconfirmed on this model** — only confirmed on the sibling; the defensive unit's journal is the intended way to settle this. - **Module search-path precedence** (`extra/` overriding in-tree `kernel/...`) is asserted from documented Fedora/kmod convention, not yet verified on this image — check `modinfo -F filename snd_hda_codec_cs8409` after the real test boot. - **Live-unbind panic risk**: a live `unbind`/`rmmod`/`modprobe`/`bind` cycle of this module while PipeWire has the sink active caused an unrecoverable freeze on the sibling model. This is exactly why all testing here goes through a full image build + reboot, never a live module reload. - **Secure Boot non-goal**: this approach assumes Secure Boot stays disabled on this hardware. If it's ever enabled, this entire approach breaks (an unsigned out-of-tree `.ko` gets rejected at load) and would need MOK enrollment + module signing added to the builder stage. - **Personal-fork provenance risk**: the ultimate origin of the 6.17+ compatibility work is an unmerged personal-fork PR (#30). Vendoring mitigates the "it could disappear" risk but doesn't change that this hasn't gone through upstream review. - `quine-linux`'s own `rpm-ostree-cleanup.timer` prunes the rollback deployment weekly — worth being aware of before testing an unverified change on this hardware, though no action has been taken on this yet. ## References - https://github.com/egorenar/snd-hda-codec-cs8409/issues/5 - https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5314393975 - https://github.com/egorenar/snd-hda-codec-cs8409/issues/5#issuecomment-5338459155 - https://github.com/egorenar/snd-hda-codec-cs8409/pull/30 - https://github.com/daviddoll/snd-hda-codec-cs8409/tree/kernel-6.17-hda-compat - https://github.com/rojoxstar/imac19-2-cs8409-linux (iMac19,2-specific project; primarily speaker/playback and Apple-DSP research, explicitly does not claim mic-capture support — investigated and set aside in favor of the fix above, which directly targets the observed symptom)