From 81e940c9269760c73f9dc6e9866c2d7d1576c970 Mon Sep 17 00:00:00 2001 From: Meir Dick Date: Thu, 6 Aug 2026 11:21:22 -0400 Subject: [PATCH 1/2] Detect Elan match-on-chip fingerprint readers again Elan readers report "ELAN:ARM-M4" as their product string, so the *fingerprint* and *biometric* checks miss them. Elan's 04f3 is also left out of the vendor list on purpose, because Elan makes touchscreens too. Both checks fail, so the machine looks like it has no reader. Add "elan:arm-m4" to the product string check. The comment above the vendor list already says the excluded vendors should still match there, so this makes that true. The vendor list and its has_kernel_driver guard are unchanged, and touchscreens still cannot cause a false positive. The string is a family name, not one device. libfprint uses it for 04f3:0c9c and 04f3:0ca7 as well as 04f3:0ca8. Tested on an HP EliteBook X G2i with 04f3:0ca8. --- bin/omarchy-hw-fingerprint | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/omarchy-hw-fingerprint b/bin/omarchy-hw-fingerprint index c5f119d9cd..337c4bfd10 100755 --- a/bin/omarchy-hw-fingerprint +++ b/bin/omarchy-hw-fingerprint @@ -30,7 +30,10 @@ for dev in /sys/bus/usb/devices/*; do if [[ -r $dev/product ]]; then product=$(<"$dev/product") product=${product,,} - [[ $product == *fingerprint* || $product == *biometric* ]] && exit 0 + # Elan's match-on-chip readers report "ELAN:ARM-M4", the family name + # rather than the function. Elan is left out of the vendor list below + # on purpose, so without this they match nothing. + [[ $product == *fingerprint* || $product == *biometric* || $product == *elan:arm-m4* ]] && exit 0 fi if [[ -r $dev/idVendor ]]; then From 37bf1255416191a13a22ff676eb917b33d850787 Mon Sep 17 00:00:00 2001 From: Meir Dick Date: Fri, 7 Aug 2026 01:06:14 -0400 Subject: [PATCH 2/2] Add HP EliteBook X G2i hardware support On a stock install this machine has no working speakers, microphone or webcam. Adds a hardware-gated script that installs the two packages from omacom-io/omarchy-pkgs#124, and the speaker tuning. The audio gate matches the model line; the camera gate also requires the OV05C10 sensor, so an EliteBook with a different camera module does not get it. The call goes after intel/ptl-kernel.sh: both packages pull DKMS modules, and the existing comment for intel-ipu7-camera gives the same reason. The tuning coefficients were decoded from HP's factory DTS:X Ultra data on the machine's own Windows partition. They are derived values; no vendor file is included. Co-Authored-By: Claude Opus 5 (1M context) --- .../tunings/hp-elitebook-x-g2i/README.md | 101 +++++++ .../hp-elitebook-x-g2i/filter-chain.conf | 258 ++++++++++++++++++ .../tunings/hp-elitebook-x-g2i/tuning.conf | 36 +++ install/hardware/all.sh | 2 + install/hardware/hp/elitebook-x-g2i.sh | 31 +++ 5 files changed, 428 insertions(+) create mode 100644 default/audio/tunings/hp-elitebook-x-g2i/README.md create mode 100644 default/audio/tunings/hp-elitebook-x-g2i/filter-chain.conf create mode 100644 default/audio/tunings/hp-elitebook-x-g2i/tuning.conf create mode 100644 install/hardware/hp/elitebook-x-g2i.sh diff --git a/default/audio/tunings/hp-elitebook-x-g2i/README.md b/default/audio/tunings/hp-elitebook-x-g2i/README.md new file mode 100644 index 0000000000..790fc2f893 --- /dev/null +++ b/default/audio/tunings/hp-elitebook-x-g2i/README.md @@ -0,0 +1,101 @@ +# HP EliteBook X G2i speaker tuning (Omarchy) + +Omarchy `omarchy-audio-tuning` profile for the HP EliteBook X G2i +(DMI product SKU `D4DU1UT#ABA`, HP subsystem ID `8E86103C`), whose four +TAS2783 smart amps sound boxy on Linux because the DSP voicing that Windows +applies in the DTS:X Ultra APO is absent. + +This tuning carries **HP's own factory EQ, bit-exact** — not a re-creation by +ear. Every biquad coefficient comes straight out of the per-board DTS tuning +data extracted from this machine's Windows partition: + + extracted-windows/audio-dts-tuning-8E86/HPCMIT8E86103C/ + +## What was decoded + +`dts_apo4_oem_config_8E86103C.xml` (plain text) declares, for the internal +stereo speakers, that the default content mode is `APO4-Music` +(`APO4-ContentMode-Default`), and that this mode enables: + +| DTS stage | Setting | Carried here as | +|---|---|---| +| `EFX:Eagle-HPF` | Enable=1, Freq=100, Order=2 | `bq_highpass` 100 Hz, Q 0.7071 (Butterworth 2nd order) | +| `EFX:Eagle-AEQ` / GPEQ | blobs `dts_gpeq_mode1_{48k,44k}` , In/Out gain 0 dB | 13 `bq_raw` sections, coefficients verbatim | +| `EFX:Eagle-MBHL` | multiband protection/loudness (envelope mode) | LSP lookahead limiter (Omarchy standard), not a multiband recreation | +| `SFX:Eagle-TBHDX` | bass enhancer, **Enable=0** in Music mode | omitted (disabled at the factory too) | + +Mode-to-blob map from the XML (internal speakers): Music→mode1, Movie→mode2, +Voice→mode3 (HPF 160 Hz), Game1/2/3→mode4/5/6, Custom→mode7. Music being the +default for media/notifications, mode1 is what Windows plays music through. + +## GPEQ blob format + +Established by decoding both sample-rate variants of every mode to mutually +consistent curves (see `../decode_dts_gpeq.py`): + +- int32 LE words; the 4720-byte file holds **two identical banks** (L/R), + the second starting at the exact midpoint. +- Bank: `word0` = section count (24), `word1` = 0, then per section six words + `[qflag, b0, b1, b2, a1, a2]`. +- `qflag` = 0 marks an unused section; otherwise coefficients are + **Q(32-qflag) fixed point** (`value / 2^(32-qflag)`; qflag 2 → Q2.30 for + the peaking sections, qflag 1 → Q1.31 for the HF tilt sections). +- Feedback convention `y[n] = Σ b_i·x[n-i] + a1·y[n-1] + a2·y[n-2]`, i.e. + `H(z) = (b0 + b1·z⁻¹ + b2·z⁻²) / (1 − a1·z⁻¹ − a2·z⁻²)`; a1/a2 are negated + in `filter-chain.conf` for PipeWire's RBJ convention. + +The decode is self-validating: the 44.1 kHz and 48 kHz banks produce the same +analog-domain curve, and each section lands on a tuning-engineer-round value: + +| # | Type | fc | Gain | Q | +|---|---|---|---|---| +| 0 | peaking | 118 Hz | +2.0 dB | 2.8 | +| 1 | peaking | 158 Hz | −3.0 dB | 3.3 | +| 2 | peaking | 254 Hz | −4.5 dB | 2.3 | +| 3 | peaking | 368 Hz | −3.5 dB | 2.0 | +| 4 | peaking | 624 Hz | −6.0 dB | 1.7 | +| 5 | peaking | 848 Hz | −5.0 dB | 1.8 | +| 6 | peaking | 1.37 kHz | −7.0 dB | 2.3 | +| 7 | peaking | 1.78 kHz | +3.0 dB | 3.6 | +| 8 | peaking | 2.90 kHz | −5.0 dB | 1.9 | +| 9 | peaking | 3.98 kHz | −3.5 dB | 5.1 | +| 10 | peaking | 5.53 kHz | −2.0 dB | 2.9 | +| 11 | peaking | 13.6 kHz | −5.0 dB | 3.3 | +| 12 | 1st-order HF tilt | — | +3.5 dB LF → −6 dB @ Nyquist | — | + +The deep 600 Hz–1.4 kHz cuts are exactly the "boxy" band; HP's engineers cut +it 5–7 dB and added presence back at 1.8 kHz. + +Rather than re-fitting these to `Freq/Q/Gain` sections, `filter-chain.conf` +uses PipeWire's `bq_raw` with the decoded coefficients for both 48000 and +44100 Hz, so the EQ stage is bit-identical to Windows' at either rate. (As +shipped by DTS, the 44.1 kHz bank sits ~2.1 dB hotter overall — a quirk of +their tilt section; the limiter absorbs it. The speaker sink runs at 48 kHz.) + +## Headroom / limiter + +The full cascade (high-pass included) peaks at **+3.66 dB at 1.86 kHz** on the +48 kHz bank. Limiter input gain is set to −4.66 dB (`g_in = 0.585`) so a +full-scale sine at the worst frequency lands exactly at the −1 dBFS threshold +(`th = 0.891`), with `alr`/`boost` disabled per Omarchy convention. The +−12 dB hardware safety trim on the `tas2783-N Speaker Volume` controls is +independent of this and stays in place. + +## Not decoded / out of scope + +- `dts_peq_ui_settings_8E86103C.bin` (gzip, 216 KB raw) and + `dts_ctc_ui_settings_8E86103C.bin` decompress to protobuf-style binary UI + state, not tuning curves. +- `dts_ctc_*.bin` — crosstalk cancellation matrices; not reproduced. +- `dts_hpeq_*.bin` — headphone EQ; this tuning only fronts the speaker sink. +- MBHL's program-dependent multiband loudness ("Boost 25", makeup gains) is a + dynamics processor, deliberately not imitated by a static EQ. + +## Install + +``` +sudo cp -r audio-tuning/hp-elitebook-x-g2i /usr/share/omarchy/default/audio/tunings/ +omarchy-audio-tuning on +``` + +Requires `lsp-plugins-lv2` for the limiter. diff --git a/default/audio/tunings/hp-elitebook-x-g2i/filter-chain.conf b/default/audio/tunings/hp-elitebook-x-g2i/filter-chain.conf new file mode 100644 index 0000000000..d6777402cc --- /dev/null +++ b/default/audio/tunings/hp-elitebook-x-g2i/filter-chain.conf @@ -0,0 +1,258 @@ +# HP EliteBook X G2i (SKU D4DU1UT#ABA) internal speaker tuning. +# +# This is HP's own per-board DTS:X Ultra ("Eagle") speaker voicing, carried +# verbatim: the biquad coefficients below are decoded from the factory blob +# dts_gpeq_mode1_{48k,44k}_8E86103C.bin (the graphic/parametric EQ bank the +# Windows DTS APO loads for the default "Music" content mode on the internal +# stereo speakers), preceded by the 100 Hz 2nd-order high-pass the same mode +# enables (EFX:Eagle-HPF Enable=1, Freq=100, Order=2 in +# dts_apo4_oem_config_8E86103C.xml), and followed by the standard Omarchy +# lookahead limiter in place of DTS's MBHL protection stage. +# +# Blob format (verified by decoding both sample-rate variants to the same +# curve): int32 LE words; word0 = section count (24), word1 = 0, then per +# section 6 words: [qflag, b0, b1, b2, a1, a2] with coefficients in +# Q(32-qflag) fixed point and the feedback terms stored for +# y[n] = ... + a1*y[n-1] + a2*y[n-2] (negated here for PipeWire's RBJ +# convention). The bank is duplicated at the file midpoint for L/R; both +# banks are identical, so the same chain serves both channels. Unused +# sections have qflag = 0. See README.md next to this file. +# +# bq_raw is used instead of fitted Freq/Q/Gain sections so the response is +# bit-exact to HP's tuning at 48 kHz and 44.1 kHz, the two rates DTS ships +# coefficients for. (The 44.1 kHz bank sits ~2.1 dB hotter overall as +# shipped by DTS; the limiter absorbs that if the graph ever runs at 44.1 +# kHz. In practice the graph follows the 48 kHz speaker sink.) +# +# Channels are wired explicitly because the limiter is a stereo plugin; a mono +# graph is duplicated per channel and would limit each side independently, +# shifting the stereo image on bass transients. + +context.modules = [ + { name = libpipewire-module-filter-chain + args = { + node.description = "Laptop Speakers" + media.name = "Laptop Speakers" + + filter.graph = { + nodes = [ + # ---- left channel ---- + # DTS Music mode enables a 100 Hz order-2 high-pass ahead of the EQ + # bank; Q 0.7071 is the Butterworth realisation of "order 2". + { type = builtin name = hp_l label = bq_highpass control = { "Freq" = 100.0 "Q" = 0.7071 } } + { type = builtin name = g0_l label = bq_raw # peaking fc 118 Hz +2.0 dB Q 2.8 + config = { coefficients = [ + { rate = 48000 b0 = 1.00072288513 b1 = -1.99416983128 b2 = 0.993692994118 a0 = 1.0 a1 = -1.99416983128 a2 = 0.994415938854 } + { rate = 44100 b0 = 1.0007866621 b1 = -1.99363219738 b2 = 0.993137001991 a0 = 1.0 a1 = -1.99363219738 a2 = 0.993923664093 } + ] } } + { type = builtin name = g1_l label = bq_raw # peaking fc 158 Hz -3.0 dB Q 3.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.999094188213 b1 = -1.99335944653 b2 = 0.994702637196 a0 = 1.0 a1 = -1.99335944653 a2 = 0.993796825409 } + { rate = 44100 b0 = 0.999014377594 b1 = -1.99273216724 b2 = 0.994235873222 a0 = 1.0 a1 = -1.99273216724 a2 = 0.993250250816 } + ] } } + { type = builtin name = g2_l label = bq_raw # peaking fc 254 Hz -4.5 dB Q 2.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.997050523758 b1 = -1.98426103592 b2 = 0.988360345364 a0 = 1.0 a1 = -1.98426103592 a2 = 0.985410928726 } + { rate = 44100 b0 = 0.996791899204 b1 = -1.98277056217 b2 = 0.98733985424 a0 = 1.0 a1 = -1.98277044296 a2 = 0.984131813049 } + ] } } + { type = builtin name = g3_l label = bq_raw # peaking fc 368 Hz -3.5 dB Q 2.0 + config = { coefficients = [ + { rate = 48000 b0 = 0.996014297009 b1 = -1.97352063656 b2 = 0.979950368404 a0 = 1.0 a1 = -1.97352063656 a2 = 0.975964605808 } + { rate = 44100 b0 = 0.995666921139 b1 = -1.9709777832 b2 = 0.978202879429 a0 = 1.0 a1 = -1.9709777832 a2 = 0.973869740963 } + ] } } + { type = builtin name = g4_l label = bq_raw # peaking fc 624 Hz -6.0 dB Q 1.7 + config = { coefficients = [ + { rate = 48000 b0 = 0.988305151463 b1 = -1.9460439682 b2 = 0.964804232121 a0 = 1.0 a1 = -1.9460439682 a2 = 0.953109502792 } + { rate = 44100 b0 = 0.987301409245 b1 = -1.94073450565 b2 = 0.961783409119 a0 = 1.0 a1 = -1.94073450565 a2 = 0.949084877968 } + ] } } + { type = builtin name = g5_l label = bq_raw # peaking fc 848 Hz -5.0 dB Q 1.8 + config = { coefficients = [ + { rate = 48000 b0 = 0.986983001232 b1 = -1.92765474319 b2 = 0.953532099724 a0 = 1.0 a1 = -1.92765474319 a2 = 0.940515041351 } + { rate = 44100 b0 = 0.985877394676 b1 = -1.9202760458 b2 = 0.949585258961 a0 = 1.0 a1 = -1.9202760458 a2 = 0.935462534428 } + ] } } + { type = builtin name = g6_l label = bq_raw # peaking fc 1.37 kHz -7.0 dB Q 2.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.979255020618 b1 = -1.89278125763 b2 = 0.945760846138 a0 = 1.0 a1 = -1.89278125763 a2 = 0.925015866756 } + { rate = 44100 b0 = 0.977528393269 b1 = -1.88076937199 b2 = 0.941246449947 a0 = 1.0 a1 = -1.88076937199 a2 = 0.918774843216 } + ] } } + { type = builtin name = g7_l label = bq_raw # peaking fc 1.78 kHz +3.0 dB Q 3.6 + config = { coefficients = [ + { rate = 48000 b0 = 1.01307702065 b1 = -1.8830935955 b2 = 0.923525154591 a0 = 1.0 a1 = -1.8830935955 a2 = 0.936602115631 } + { rate = 44100 b0 = 1.01415884495 b1 = -1.86829900742 b2 = 0.917198777199 a0 = 1.0 a1 = -1.86829900742 a2 = 0.931357502937 } + ] } } + { type = builtin name = g8_l label = bq_raw # peaking fc 2.90 kHz -5.0 dB Q 1.9 + config = { coefficients = [ + { rate = 48000 b0 = 0.959467947483 b1 = -1.67663598061 b2 = 0.855309844017 a0 = 1.0 a1 = -1.67663598061 a2 = 0.8147777915 } + { rate = 44100 b0 = 0.956516981125 b1 = -1.63998699188 b2 = 0.844775497913 a0 = 1.0 a1 = -1.63998699188 a2 = 0.801292479038 } + ] } } + { type = builtin name = g9_l label = bq_raw # peaking fc 3.98 kHz -3.5 dB Q 5.1 + config = { coefficients = [ + { rate = 48000 b0 = 0.983916223049 b1 = -1.64805448055 b2 = 0.919093132019 a0 = 1.0 a1 = -1.64805448055 a2 = 0.903009355068 } + { rate = 44100 b0 = 0.982770562172 b1 = -1.59883213043 b2 = 0.913330078125 a0 = 1.0 a1 = -1.59883213043 a2 = 0.896100640297 } + ] } } + { type = builtin name = g10_l label = bq_raw # peaking fc 5.53 kHz -2.0 dB Q 2.9 + config = { coefficients = [ + { rate = 48000 b0 = 0.977126538754 b1 = -1.32099425793 b2 = 0.800446629524 a0 = 1.0 a1 = -1.32099425793 a2 = 0.777573227882 } + { rate = 44100 b0 = 0.975850343704 b1 = -1.2404794693 b2 = 0.789312839508 a0 = 1.0 a1 = -1.2404794693 a2 = 0.765163242817 } + ] } } + { type = builtin name = g11_l label = bq_raw # peaking fc 13.6 kHz -5.0 dB Q 3.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.909254550934 b1 = 0.309279620647 b2 = 0.676059544086 a0 = 1.0 a1 = 0.309279620647 a2 = 0.58531409502 } + { rate = 44100 b0 = 0.910680770874 b1 = 0.437900751829 b2 = 0.681150853634 a0 = 1.0 a1 = 0.437900751829 a2 = 0.591831684113 } + ] } } + { type = builtin name = g12_l label = bq_raw # first-order HF tilt (+3.5 dB LF shelf falling to -6 dB at Nyquist) + config = { coefficients = [ + { rate = 48000 b0 = 0.999999999534 b1 = 0.500000000931 b2 = 0 a0 = 1.0 a1 = -0 a2 = -0 } + { rate = 44100 b0 = 0.999999999534 b1 = 0.918762207497 b2 = 0 a0 = 1.0 a1 = -0 a2 = -0 } + ] } } + # ---- right channel ---- + # DTS Music mode enables a 100 Hz order-2 high-pass ahead of the EQ + # bank; Q 0.7071 is the Butterworth realisation of "order 2". + { type = builtin name = hp_r label = bq_highpass control = { "Freq" = 100.0 "Q" = 0.7071 } } + { type = builtin name = g0_r label = bq_raw # peaking fc 118 Hz +2.0 dB Q 2.8 + config = { coefficients = [ + { rate = 48000 b0 = 1.00072288513 b1 = -1.99416983128 b2 = 0.993692994118 a0 = 1.0 a1 = -1.99416983128 a2 = 0.994415938854 } + { rate = 44100 b0 = 1.0007866621 b1 = -1.99363219738 b2 = 0.993137001991 a0 = 1.0 a1 = -1.99363219738 a2 = 0.993923664093 } + ] } } + { type = builtin name = g1_r label = bq_raw # peaking fc 158 Hz -3.0 dB Q 3.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.999094188213 b1 = -1.99335944653 b2 = 0.994702637196 a0 = 1.0 a1 = -1.99335944653 a2 = 0.993796825409 } + { rate = 44100 b0 = 0.999014377594 b1 = -1.99273216724 b2 = 0.994235873222 a0 = 1.0 a1 = -1.99273216724 a2 = 0.993250250816 } + ] } } + { type = builtin name = g2_r label = bq_raw # peaking fc 254 Hz -4.5 dB Q 2.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.997050523758 b1 = -1.98426103592 b2 = 0.988360345364 a0 = 1.0 a1 = -1.98426103592 a2 = 0.985410928726 } + { rate = 44100 b0 = 0.996791899204 b1 = -1.98277056217 b2 = 0.98733985424 a0 = 1.0 a1 = -1.98277044296 a2 = 0.984131813049 } + ] } } + { type = builtin name = g3_r label = bq_raw # peaking fc 368 Hz -3.5 dB Q 2.0 + config = { coefficients = [ + { rate = 48000 b0 = 0.996014297009 b1 = -1.97352063656 b2 = 0.979950368404 a0 = 1.0 a1 = -1.97352063656 a2 = 0.975964605808 } + { rate = 44100 b0 = 0.995666921139 b1 = -1.9709777832 b2 = 0.978202879429 a0 = 1.0 a1 = -1.9709777832 a2 = 0.973869740963 } + ] } } + { type = builtin name = g4_r label = bq_raw # peaking fc 624 Hz -6.0 dB Q 1.7 + config = { coefficients = [ + { rate = 48000 b0 = 0.988305151463 b1 = -1.9460439682 b2 = 0.964804232121 a0 = 1.0 a1 = -1.9460439682 a2 = 0.953109502792 } + { rate = 44100 b0 = 0.987301409245 b1 = -1.94073450565 b2 = 0.961783409119 a0 = 1.0 a1 = -1.94073450565 a2 = 0.949084877968 } + ] } } + { type = builtin name = g5_r label = bq_raw # peaking fc 848 Hz -5.0 dB Q 1.8 + config = { coefficients = [ + { rate = 48000 b0 = 0.986983001232 b1 = -1.92765474319 b2 = 0.953532099724 a0 = 1.0 a1 = -1.92765474319 a2 = 0.940515041351 } + { rate = 44100 b0 = 0.985877394676 b1 = -1.9202760458 b2 = 0.949585258961 a0 = 1.0 a1 = -1.9202760458 a2 = 0.935462534428 } + ] } } + { type = builtin name = g6_r label = bq_raw # peaking fc 1.37 kHz -7.0 dB Q 2.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.979255020618 b1 = -1.89278125763 b2 = 0.945760846138 a0 = 1.0 a1 = -1.89278125763 a2 = 0.925015866756 } + { rate = 44100 b0 = 0.977528393269 b1 = -1.88076937199 b2 = 0.941246449947 a0 = 1.0 a1 = -1.88076937199 a2 = 0.918774843216 } + ] } } + { type = builtin name = g7_r label = bq_raw # peaking fc 1.78 kHz +3.0 dB Q 3.6 + config = { coefficients = [ + { rate = 48000 b0 = 1.01307702065 b1 = -1.8830935955 b2 = 0.923525154591 a0 = 1.0 a1 = -1.8830935955 a2 = 0.936602115631 } + { rate = 44100 b0 = 1.01415884495 b1 = -1.86829900742 b2 = 0.917198777199 a0 = 1.0 a1 = -1.86829900742 a2 = 0.931357502937 } + ] } } + { type = builtin name = g8_r label = bq_raw # peaking fc 2.90 kHz -5.0 dB Q 1.9 + config = { coefficients = [ + { rate = 48000 b0 = 0.959467947483 b1 = -1.67663598061 b2 = 0.855309844017 a0 = 1.0 a1 = -1.67663598061 a2 = 0.8147777915 } + { rate = 44100 b0 = 0.956516981125 b1 = -1.63998699188 b2 = 0.844775497913 a0 = 1.0 a1 = -1.63998699188 a2 = 0.801292479038 } + ] } } + { type = builtin name = g9_r label = bq_raw # peaking fc 3.98 kHz -3.5 dB Q 5.1 + config = { coefficients = [ + { rate = 48000 b0 = 0.983916223049 b1 = -1.64805448055 b2 = 0.919093132019 a0 = 1.0 a1 = -1.64805448055 a2 = 0.903009355068 } + { rate = 44100 b0 = 0.982770562172 b1 = -1.59883213043 b2 = 0.913330078125 a0 = 1.0 a1 = -1.59883213043 a2 = 0.896100640297 } + ] } } + { type = builtin name = g10_r label = bq_raw # peaking fc 5.53 kHz -2.0 dB Q 2.9 + config = { coefficients = [ + { rate = 48000 b0 = 0.977126538754 b1 = -1.32099425793 b2 = 0.800446629524 a0 = 1.0 a1 = -1.32099425793 a2 = 0.777573227882 } + { rate = 44100 b0 = 0.975850343704 b1 = -1.2404794693 b2 = 0.789312839508 a0 = 1.0 a1 = -1.2404794693 a2 = 0.765163242817 } + ] } } + { type = builtin name = g11_r label = bq_raw # peaking fc 13.6 kHz -5.0 dB Q 3.3 + config = { coefficients = [ + { rate = 48000 b0 = 0.909254550934 b1 = 0.309279620647 b2 = 0.676059544086 a0 = 1.0 a1 = 0.309279620647 a2 = 0.58531409502 } + { rate = 44100 b0 = 0.910680770874 b1 = 0.437900751829 b2 = 0.681150853634 a0 = 1.0 a1 = 0.437900751829 a2 = 0.591831684113 } + ] } } + { type = builtin name = g12_r label = bq_raw # first-order HF tilt (+3.5 dB LF shelf falling to -6 dB at Nyquist) + config = { coefficients = [ + { rate = 48000 b0 = 0.999999999534 b1 = 0.500000000931 b2 = 0 a0 = 1.0 a1 = -0 a2 = -0 } + { rate = 44100 b0 = 0.999999999534 b1 = 0.918762207497 b2 = 0 a0 = 1.0 a1 = -0 a2 = -0 } + ] } } + { type = lv2 + name = limiter + plugin = "http://lsp-plug.in/plugins/lv2/limiter_stereo" + control = { + # Both default to enabled: "alr" regulates level toward the + # threshold and "boost" normalises the threshold up to full + # scale. A fixed tuning must switch them off or its tone drifts + # with programme level. + "alr" = 0 + "boost" = 0 + # The EQ cascade peaks at +3.66 dB (1.86 kHz, 48 kHz bank, HPF + # included), so -4.66 dB of input gain puts a full-scale sine at + # the worst frequency exactly at the -1 dBFS threshold. + "g_in" = 0.585 + "th" = 0.891 + } + } + ] + + links = [ + { output = "hp_l:Out" input = "g0_l:In" } + { output = "g0_l:Out" input = "g1_l:In" } + { output = "g1_l:Out" input = "g2_l:In" } + { output = "g2_l:Out" input = "g3_l:In" } + { output = "g3_l:Out" input = "g4_l:In" } + { output = "g4_l:Out" input = "g5_l:In" } + { output = "g5_l:Out" input = "g6_l:In" } + { output = "g6_l:Out" input = "g7_l:In" } + { output = "g7_l:Out" input = "g8_l:In" } + { output = "g8_l:Out" input = "g9_l:In" } + { output = "g9_l:Out" input = "g10_l:In" } + { output = "g10_l:Out" input = "g11_l:In" } + { output = "g11_l:Out" input = "g12_l:In" } + { output = "g12_l:Out" input = "limiter:in_l" } + { output = "hp_r:Out" input = "g0_r:In" } + { output = "g0_r:Out" input = "g1_r:In" } + { output = "g1_r:Out" input = "g2_r:In" } + { output = "g2_r:Out" input = "g3_r:In" } + { output = "g3_r:Out" input = "g4_r:In" } + { output = "g4_r:Out" input = "g5_r:In" } + { output = "g5_r:Out" input = "g6_r:In" } + { output = "g6_r:Out" input = "g7_r:In" } + { output = "g7_r:Out" input = "g8_r:In" } + { output = "g8_r:Out" input = "g9_r:In" } + { output = "g9_r:Out" input = "g10_r:In" } + { output = "g10_r:Out" input = "g11_r:In" } + { output = "g11_r:Out" input = "g12_r:In" } + { output = "g12_r:Out" input = "limiter:in_r" } + ] + + inputs = [ "hp_l:In" "hp_r:In" ] + outputs = [ "limiter:out_l" "limiter:out_r" ] + } + + audio.channels = 2 + audio.position = [ FL FR ] + + capture.props = { + node.name = "omarchy_speaker_tuning" + media.class = Audio/Sink + } + playback.props = { + node.name = "omarchy_speaker_tuning_output" + node.passive = true + target.object = "@SPEAKER_SINK@" + # This stream is the filter's output and is a movable sink input like any + # other, so anything that reroutes "all streams" to a newly selected + # output would drag the processing along with it -- onto headphones, or + # into the tuning's own sink, which is a cycle. Pin it. + node.dont-move = true + # If the speaker sink is not present yet -- the tuning host can start + # before the device is discovered -- WirePlumber would otherwise link this + # output to whatever default exists, quietly tuning the wrong device while + # the tuning sink still looks healthy. Wait for the named target instead. + # Both are needed: without linger, WirePlumber destroys the node rather + # than waiting (see its scripts/linking/find-defined-target.lua). + node.dont-fallback = true + node.linger = true + } + } + } +] diff --git a/default/audio/tunings/hp-elitebook-x-g2i/tuning.conf b/default/audio/tunings/hp-elitebook-x-g2i/tuning.conf new file mode 100644 index 0000000000..9835fac51d --- /dev/null +++ b/default/audio/tunings/hp-elitebook-x-g2i/tuning.conf @@ -0,0 +1,36 @@ +## HP EliteBook X G2i internal speakers (quad TAS2783 smart amps). +## +## A 100 Hz order-2 high-pass, thirteen biquads carried bit-exact from HP's +## factory DTS:X Ultra tuning, and a lookahead limiter, applied as a PipeWire +## filter-chain in front of the internal speaker sink. The stock Linux path +## already programs the TAS2783 amps (with the -12 dB safety trim on the +## per-amp Speaker Volume controls); this adds the perceptual voicing the +## Windows DTS APO layer provides and Linux does not. + +description="HP EliteBook X G2i speakers" +## Matched on the DMI product SKU, the narrowest identifier for this board. +## The DTS tuning is keyed per-board by HP (subsystem ID 8E86103C), so this +## must not widen to other EliteBook models whose speakers differ. +## +## D4DU1UT#ABA EliteBook X G2i -- the board the DTS data was extracted from +match_sku=("D4DU1UT#ABA") +## Unescaped dots: this is passed to awk as a string, where a backslash escape +## would be consumed before the regex sees it. +sink_pattern='^alsa_output.*sof_sdw.*HiFi__Speaker__sink$' + +## Provenance. Decoded from HP's own per-board DTS:X Ultra ("Eagle") tuning +## data extracted from this machine's Windows partition: +## extracted-windows/audio-dts-tuning-8E86/HPCMIT8E86103C/. The EQ biquads are +## the factory dts_gpeq_mode1_{48k,44k}_8E86103C.bin banks (the default +## "Music" content mode for the internal stereo speakers, per +## dts_apo4_oem_config_8E86103C.xml), used verbatim rather than re-fitted, so +## the magnitude response is bit-exact to Windows' EQ stage at both shipped +## sample rates. DTS's MBHL protection/loudness stage and TBHDX bass enhancer +## are not reproduced; the Omarchy lookahead limiter stands in for protection. +derived_from="HP/DTS factory tuning blob dts_gpeq_mode1 (SSID 8E86103C), decoded" +validated_hardware="HP EliteBook X G2i (D4DU1UT#ABA, SSID 8E86103C)" + +## Headroom: the EQ cascade (high-pass included) peaks at +3.66 dB at 1.86 kHz +## on the 48 kHz bank. The limiter input gain of -4.66 dB places a full-scale +## sine at that frequency exactly at the -1 dBFS limiter threshold. +limiter_headroom_db="0.0" diff --git a/install/hardware/all.sh b/install/hardware/all.sh index 9a9f1098ce..983cd174ac 100644 --- a/install/hardware/all.sh +++ b/install/hardware/all.sh @@ -32,6 +32,8 @@ run_logged "$OMARCHY_INSTALL/hardware/asus/fix-z13-touchpad.sh" run_logged "$OMARCHY_INSTALL/hardware/framework/qmk-hid.sh" +run_logged "$OMARCHY_INSTALL/hardware/hp/elitebook-x-g2i.sh" + run_logged "$OMARCHY_INSTALL/hardware/apple/fix-spi-keyboard.sh" run_logged "$OMARCHY_INSTALL/hardware/apple/fix-suspend-nvme.sh" run_logged "$OMARCHY_INSTALL/hardware/apple/fix-t2.sh" diff --git a/install/hardware/hp/elitebook-x-g2i.sh b/install/hardware/hp/elitebook-x-g2i.sh new file mode 100644 index 0000000000..6879606b74 --- /dev/null +++ b/install/hardware/hp/elitebook-x-g2i.sh @@ -0,0 +1,31 @@ +# HP EliteBook X G2i (Panther Lake) — speakers, mic and webcam +# +# This board ships hardware that no stock Linux path drives yet: +# +# Audio Quad TAS2783 smart amps + RT712 SDCA. Panther Lake's ACPI match table +# has no entry for this pairing, so the machine falls back to a +# barebones machine driver and NO Speaker device appears at all -- not +# quiet, absent. The package adds the match-table entry via DKMS (so it +# survives kernel upgrades), a UCM profile, and the firmware filename +# links the tas2783 driver asks for but linux-firmware does not ship. +# +# Webcam OmniVision OV05C10 on IPU7. Intel's CamHAL cannot drive it: its graph +# settings binary is built for a 2944-wide readout while the sensor +# exposes 2888x1808, so the ISP receives a negative crop and emits +# black frames. The package bypasses psys/CamHAL and does debayer, auto +# exposure and white balance in userspace instead. See its README for +# the trade-offs -- it is a workaround, not a fix, and the underlying +# bug is filed against intel/ipu7-camera-hal. +# +# Both are gated on the model string so they cannot touch other EliteBooks, +# whose speaker layout and camera module differ. + +if omarchy-hw-match "EliteBook X G2i"; then + omarchy-pkg-add hp-elitebook-x-g2i-audio + + # The camera stack only makes sense where the IPU7 + OV05C10 pairing is + # actually present; the audio fix applies to the whole model line. + if grep -q "OVTI05C1" /sys/bus/acpi/devices/*/hid 2>/dev/null; then + omarchy-pkg-add hp-elitebook-x-g2i-camera + fi +fi