Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

native: stop enabling UCM Headphones at boot — was silencing speakers

Logs from the G7 (Drawcia JSL) showed everything my code expected:

UCM Speaker PCM: raw=_ucm0002.hw:sofrt5682,0 -> hw:0,0
UCM: _enadev=Speaker ok
Parallel PCM opened: hw:0,1 — auto-routing enabled
max98357a MX98360A:00: set sdmode to 1 (at 8.3s)
max98357a MX98360A:00: set sdmode to 0 (at 35.5s, never comes back)

The amp was *getting* powered up (sdmode=1), then DAPM silenced it
because the codec's `Headphone Jack Switch` was ON. Root cause: I was
enumerating every UCM SectionDevice and running `_enadev Headphones`,
which ran the ChromeOS EnableSequence `cset "name='Headphone Jack
Switch' on" + HPOL/HPOR=1` — that re-enabled the HP path after
rt5682-init's BootSequence had explicitly disabled it.

Fix:
- Skip Headphones/Headset/Headphone in UCM _enadev enumeration
- Gate the parallel headphone PCM tee behind AC_AUDIO_TEE=1
(opening hw:0,1 also powers the HP DAPM path)
- Volume keys: add DAC1/PGA*.0 * Master to try-list since this card
has no "Master"/"Speaker"/"Headphone"/"PCM" mixer elements

Jack-plug auto-routing becomes a follow-up (needs a jack-state watcher
thread that calls _enadev Headphones / _disdev Speaker on plug).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+54 -18
+54 -18
fedac/native/src/audio.c
··· 2061 2061 audio->headphone_pcm = NULL; 2062 2062 { 2063 2063 const char *secondary = NULL; 2064 - if (ucm_speaker_pcm[0] && ucm_headphone_pcm[0] && 2064 + /* Opt-in: opening the headphone PCM at boot was powering up 2065 + * the HP DAPM path even with UCM Headphones disabled, which 2066 + * overrode jack-sense and silenced the MAX98360A amp. Keep 2067 + * the secondary PCM closed by default; a future jack-watcher 2068 + * thread will open/close it in response to plug events. Set 2069 + * AC_AUDIO_TEE=1 to force-open anyway (for ThinkPad etc. 2070 + * single-PCM HDA hardware, where it's a noop). */ 2071 + const char *tee_env = getenv("AC_AUDIO_TEE"); 2072 + int tee_enabled = (tee_env && tee_env[0] == '1'); 2073 + if (tee_enabled && ucm_speaker_pcm[0] && ucm_headphone_pcm[0] && 2065 2074 strcmp(ucm_speaker_pcm, ucm_headphone_pcm) != 0) { 2066 2075 /* Pick whichever the main PCM didn't open. */ 2067 2076 if (strstr(audio->audio_device, ucm_speaker_pcm)) ··· 2195 2204 } else { 2196 2205 fprintf(stderr, "[audio] UCM: _verb=HiFi failed\n"); 2197 2206 } 2198 - /* Enable both Speaker and Headphone — DAPM picks the live 2199 - * endpoint based on jack-sense, so enabling both at init 2200 - * gives the amp its PMU event and a later unplug/plug of 2201 - * headphones still routes correctly. */ 2202 - /* Enumerate all SectionDevice entries and try them all. 2203 - * Upstream UCM fragments are inconsistent about singular 2204 - * vs plural (sof-rt5682/rt5682-headset.conf declares 2205 - * "Headphones" not "Headphone"; sof-ssp_amp uses 2206 - * "Speaker"; sof-cs42l42 uses "Headphone"). Rather than 2207 - * hardcode guesses, ask ALSA what this card's UCM 2208 - * declares and try to enable each — verb state only 2209 - * changes on ok, so extra failed attempts are free. */ 2207 + /* Enable ONLY Speaker at boot — enumerating every device 2208 + * (including Headphones/Headset) was running ChromeOS 2209 + * UCM EnableSequences that set `Headphone Jack Switch on` 2210 + * + `HPOL/HPOR Playback Switch 1`. That forces DAPM to 2211 + * route audio through the RT5682 headphone path and 2212 + * powers down the MAX98360A amp (kmsg showed `sdmode 2213 + * to 0` at 35s and never recovering). 2214 + * 2215 + * The rt5682-init BootSequence from WeirdTreeThing's 2216 + * UCM deliberately ships with HP jack/switch OFF so 2217 + * the speaker amp stays live when nothing is plugged 2218 + * in. Keep that intact — only run the Speaker (and 2219 + * mic) EnableSequence, not any headphone one. Jack- 2220 + * plug routing becomes a later follow-up (a jack- 2221 + * state watcher thread that flips _enadev on plug). */ 2210 2222 const char **devlist = NULL; 2211 2223 int ndev = snd_use_case_get_list(uc, "_devices/HiFi", 2212 2224 &devlist); 2225 + int enabled_speaker = 0; 2213 2226 if (ndev > 0 && devlist) { 2214 2227 for (int i = 0; i < ndev; i += 2) { 2215 2228 const char *dev = devlist[i]; 2216 2229 if (!dev || !dev[0]) continue; 2230 + /* Skip anything that would re-enable the 2231 + * headphone path at boot. Speakers first, 2232 + * mics/HDMI are safe (no DAPM routing to HP). */ 2233 + if (strstr(dev, "Headphone") || 2234 + strstr(dev, "Headset") || 2235 + strstr(dev, "Headphones")) { 2236 + fprintf(stderr, 2237 + "[audio] UCM: skip _enadev=%s (jack-gated)\n", 2238 + dev); 2239 + continue; 2240 + } 2217 2241 int rr = snd_use_case_set(uc, "_enadev", dev); 2218 2242 fprintf(stderr, "[audio] UCM: _enadev=%s %s\n", 2219 2243 dev, rr == 0 ? "ok" : "FAIL"); 2244 + if (rr == 0 && (strstr(dev, "Speaker") || 2245 + strstr(dev, "Speakers"))) 2246 + enabled_speaker = 1; 2220 2247 } 2221 2248 snd_use_case_free_list(devlist, ndev); 2222 2249 } else { 2223 - /* Fallback: try common device names manually. */ 2224 - const char *names[] = {"Speaker", "Speakers", 2225 - "Headphone", "Headphones", 2226 - "HDMI", NULL}; 2250 + /* Fallback: enable Speaker variants only. */ 2251 + const char *names[] = {"Speaker", "Speakers", NULL}; 2227 2252 for (int i = 0; names[i]; i++) { 2228 2253 if (snd_use_case_set(uc, "_enadev", names[i]) == 0) { 2229 2254 fprintf(stderr, "[audio] UCM: _enadev=%s ok\n", 2230 2255 names[i]); 2256 + enabled_speaker = 1; 2231 2257 } 2232 2258 } 2233 2259 } 2260 + if (!enabled_speaker) 2261 + fprintf(stderr, "[audio] UCM: WARNING no Speaker device enabled\n"); 2234 2262 snd_use_case_mgr_close(uc); 2235 2263 } else { 2236 2264 fprintf(stderr, "[audio] UCM: no config matched card '%s' — manual mixer fallback\n", ··· 3242 3270 // Adjust ALL playback volume elements — on Realtek ALC codecs, 3243 3271 // Master controls digital gain, Speaker/Headphone control the amplifier. 3244 3272 // Both need to be set for audible volume change. 3245 - const char *try_names[] = {"Master", "Speaker", "Headphone", "PCM", NULL}; 3273 + /* RT5682 exposes "DAC1" for digital volume; SOF cards expose 3274 + * "PGA*.0 * Master" pipeline PGAs. HDA laptops expose Master/PCM. 3275 + * Try everything — first match wins but we run through the whole 3276 + * list so volume keys work regardless of hardware. */ 3277 + const char *try_names[] = {"Master", "Speaker", "Headphone", "PCM", 3278 + "DAC1", "DAC2", 3279 + "PGA1.0 1 Master", "PGA2.0 2 Master", 3280 + "PGA5.0 5 Master", "PGA6.0 6 Master", 3281 + "PGA7.0 7 Master", NULL}; 3246 3282 int adjusted = 0; 3247 3283 for (int n = 0; try_names[n]; n++) { 3248 3284 snd_mixer_elem_t *elem = NULL;