Skip to content

Dell S2725DSM: DDC volume (VCP 0x62) reads max 65380 instead of 100 — volume keys only toggle mute/max #162

Closed

Description

@ohmyangboy

维护者你好,我使用Crisp有一段时间了,我喜欢它的简洁,但最近我遇到一个头疼的问题,就是我无法用我的Mac调节音量,我为此换了几个线材,最后在C to DP上发现可以调节。但只能调节从0到100%。我的显示器是戴尔。我自行诊断了一下,然后发现可能这是一个缺失的特性或者bug。非常希望Crisp能支持。但我现在不得不用回BetterDisplay了。

以下是Agent调研的结果。和我的诊断报告。:


English translation (for reference)

Hello maintainer. I have been using Crisp for a while and I really like how simple it is, but recently I ran into a frustrating problem: I could not adjust the volume from my Mac. I tried several different cables, and eventually found that with USB-C to DP (C to DP) I can adjust it — but only between 0% and 100%. My monitor is a Dell. I diagnosed it myself and it looks like this may be a missing feature or a bug. I would really like Crisp to support it. But for now I have had to go back to BetterDisplay.

Below is the result of an AI agent's investigation, along with my diagnostic report.


🤖 AI-generated report notice: Everything below this line was compiled by an AI agent (OpenCode) from my system diagnostics and macOS unified logs, at my request. I reviewed it before posting. It contains no serial numbers, UUIDs, usernames, file paths, or other identifying data.

What happened

With a Dell S2725DSM connected over a direct USB-C → DisplayPort cable (no dock or hub), Crisp's volume control for this monitor reads an implausible max for VCP 0x62 (65380 / 0xFF64) even though the display's real volume range is 0–100.

Crisp's write pump scales the requested percentage by that max, so nearly every step produces an out-of-range value, which the monitor clamps to 100. Result: the volume keys and the slider only produce two audible states — mute or full volume — with no intermediate levels. Brightness (VCP 0x10) works fine. This reproduces across launches (probe values observed: 38/65380, 44/65380, 44/65380).

Steps to reproduce

  1. Connect a Dell S2725DSM via USB-C → DisplayPort directly to the Mac (DDC/CI enabled on the monitor).
  2. Set the monitor as the default audio output.
  3. Open Crisp: a volume slider appears for the display (the probe "succeeds").
  4. Move the slider or press the volume keys.
  5. Observed: each step jumps between 0 (mute) and 100; no gradations.

Crisp version

1.6.0

macOS version

27.0 (26A428)

Mac model and chip

MacBook Air (Mac14,2) — Apple M2

Monitor(s) and how they are connected

Dell S2725DSM, vendor 0x10AC product 0xD1C1, connected directly via a USB-C → DisplayPort cable (no dock/adapter). DDC/CI is enabled. It is the default audio output device.

Anything else

Log evidence (captured with log show --predicate 'subsystem == "com.crisp.app"' --info --debug --style compact --last 2h):

[com.crisp.app:ddc] pairing: display 2 (vendor 0x10AC product 0xD1C1) -> channel 0 of 1 by identity
[com.crisp.app:ddc] read 0x62 display 2: ok 44/65380
[com.crisp.app:volume] DELL S2725DSM: volume probe ok 44/65380, slider shown
[com.crisp.app:keys] key tap armed
Crisp log excerpt — the same wrong max on every launch
[com.crisp.app:ddc] pairing: display 2 (vendor 0x10AC product 0xD1C1) -> channel 0 of 1 by identity
[com.crisp.app:ddc] read 0x62 display 2: ok 38/65380
[com.crisp.app:keys] key tap armed
[com.crisp.app:ddc] pairing: display 2 (vendor 0x10AC product 0xD1C1) -> channel 0 of 1 by identity
[com.crisp.app:ddc] read 0x62 display 2: ok 44/65380
[com.crisp.app:volume] DELL S2725DSM: volume probe ok 44/65380, slider shown
[com.crisp.app:keys] key tap armed
[com.crisp.app:ddc] pairing: display 2 (vendor 0x10AC product 0xD1C1) -> channel 0 of 1 by identity
[com.crisp.app:ddc] read 0x62 display 2: ok 44/65380
[com.crisp.app:volume] DELL S2725DSM: volume probe ok 44/65380, slider shown
[com.crisp.app:keys] key tap armed

A fuller log show capture (307 lines, ddc/volume/keys categories) is available on request.

Cross-checks on the same display and connection with another DDC tool (m1ddc):

  • m1ddc max volume → 100; m1ddc get volume → 44 (matches Crisp's current value — only the max field is wrong)
  • m1ddc set volume 40 / 60 / 50 → each reads back exactly and produces proportional speaker volume
  • m1ddc set volume 200 → stores 100 (m1ddc does not clamp in set, so the monitor itself clamps to a real max of 100)
  • BetterDisplay (another DDC tool) also reads 65380 for this display (its stored highValue@volume-DDCController was 65380). After overriding that stored max to 100, BetterDisplay's volume keys control this monitor correctly with gradations.

Suggested fix (hypothesis)

Sanitize implausible VCP 0x62 max values. DDCService.arm64Read accepts the reply (header and checksum valid), but bytes 6–7 decode to 65380; VolumeService.refreshVolume stores it (self.ddcMax[id] = result.max) and pump(for:) then computes raw = percent / 100 * ddcMax[id]. Treating an implausible volume max (e.g. > 255) as the standard 100 — the same fallback PR #13 introduced for invalid frames — would fix this. (m1ddc effectively behaves this way.)

Additional observation

After BetterDisplay wrote a value derived from the bad max (50% × 65380 = 32690), the monitor's 0x62 replies stopped validating (reads returned no valid reply) and the monitor was found muted; it recovered after un-muting from the monitor's own OSD. Out-of-range writes from the bad max do not appear to be harmless.

Related: #88 (monitor volume not adjustable on a different display) — though here the probe succeeds, it just reports a wrong max.

Activity

  1. didriksg commented on Sep 10, 2026

    @didriksg
    Owner

    Your log and the m1ddc check found this without a round trip, thanks. Sorry it sent you back to BetterDisplay.

    Your Dell answers the volume read with a maximum of 0xFF64. The low byte, 0x64, is the real maximum of 100, and the high byte is filled with 0xFF. Crisp took the whole value, 65380, as the top of the range, so every level above zero went out past 100 and the monitor clamped it to full volume. m1ddc 1.2.0, the version Homebrew ships, reads only the low byte, which is why it said 100. BetterDisplay reads both bytes like Crisp, which is why it showed 65380 too.

    Speaker volume is a one-byte value, so Crisp now takes the maximum from the low byte alone (#163). Crisp then only ever sends 0 to 100, so it also stays clear of the kind of out-of-range write that muted your monitor when BetterDisplay sent 32690. Keep the USB-C to DisplayPort cable: DDC only works through cables and adapters that carry it, and many don't.

    You don't have to wait for the release to try it. Here's a test build with the fix: Crisp-1.6.0.1-test.zip. It's signed and notarized like a release, it just isn't published. Quit Crisp, unzip the file, open the DMG and drag Crisp to Applications to replace your copy, then open it again. The panel shows it as 1.6.0.1, and the next real release will still reach you through the normal update.

    Could you tell me whether the volume keys and the slider step properly with it? I'll keep this open until then.

  2. ohmyangboy commented on Sep 10, 2026

    @ohmyangboy
    Author

    Tested the 1.6.0.1 test build on the same setup (Dell S2725DSM over USB-C → DisplayPort, set as the default audio output). It works — the volume keys and the slider now step through the levels properly instead of jumping between mute and full, and everything behaves as expected.

    The log confirms it on the read side too; the volume probe now reports the max as 100:

    [com.crisp.app:volume] DELL S2725DSM: volume probe ok 25/100, slider shown
    

    Thanks for the quick fix, and for the cable advice — I'll stay on the USB-C to DisplayPort connection. Looking forward to the next release.

  3. didriksg commented on Sep 10, 2026

    @didriksg
    Owner

    Thanks for testing it so quickly, and for including the log line: 25/100 is exactly what it should read now. The fix is on main and ships in the next release, so I'm closing this. If the volume acts up again on the release, reopen it.

  4. ohmyangboy commented on Oct 10, 2026

    @ohmyangboy
    Author

    Follow-up PR: #217 adds a per-display DDC Value Range for finer volume control on the Dell S2725DSM and fixes stale display IDs after reconnecting. Tested locally with the volume keys and slider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions