Skip to content

Samsung: enable all 4 speakers and fix rotation on CS35L41 tablets - #87

Open
bod09 wants to merge 2 commits into
TrebleDroid:android-16.0from
bod09:samsung-quad-speaker-rotation
Open

Samsung: enable all 4 speakers and fix rotation on CS35L41 tablets#87
bod09 wants to merge 2 commits into
TrebleDroid:android-16.0from
bod09:samsung-quad-speaker-rotation

Conversation

@bod09

@bod09 bod09 commented Jul 26, 2026

Copy link
Copy Markdown

On the Galaxy Tab S6 (and other Samsung tablets with four Cirrus CS35L41 amps) every GSI I have tried only drives 2 of the 4 speakers, and stereo is wrong in landscape. I dug into it with a rooted build and it turned out to be two separate things in the vendor audio path, both fixable from userspace.

What is going on:

  1. The four amps are named FL/FR/RL/RR and each reads one TDM slot via its "<x> ASPRX1 Slot Position" mixer control. On a plain 2 channel stereo stream only slots 0 (L) and 1 (R) carry audio. The two "front" amps default to slots 0/1 so they play, but the two "rear" amps default to slots 2/3, which are empty, so those two speakers stay silent. Pointing all four amps at slots 0/1 makes every speaker play.

  2. With four speakers at the four corners, correct stereo needs a different L/R split per orientation: in portrait the left channel is the left column of speakers, but in landscape it is a whole row. The vendor HAL never re-maps this on rotation (its only lever is a plain L/R "Swap channel" mirror, which cannot turn a column into a row), so even once all four play, landscape is wrong.

The fix here is a tiny service, phh-spkrot, that sets the four ASPRX1 slot controls. It sets the portrait mapping (which also enables all four speakers) and then updates the mapping whenever the display rotation changes, so imaging is correct in all four orientations. It runs in phhsu_daemon so it can read the rotation (dumpsys window) and write the mixer, and it self-noops on any device that does not have these controls, so it is safe to ship for everyone.

I mapped the amps to physical corners by ear (muting three at a time): FL is bottom-left, FR is bottom-right, RL is top-left, RR is top-right. The resulting per-rotation slot table (slot 0 = Left, 1 = Right):

rotation 0 (portrait)          L = left column  (FL,RL)  -> 0 1 0 1
rotation 1 (landscape)         L = top row      (RL,RR)  -> 1 1 0 0
rotation 2 (portrait flipped)  L = right column (FR,RR)  -> 1 0 1 0
rotation 3 (landscape)         L = bottom row   (FL,FR)  -> 0 0 1 1

Verified on SM-T860 (LineageOS 23.2 GSI): all four speakers play, and the left-only test tone stays on the physical left through a full turn with auto-rotate on. Rotations 0, 1 and 3 were confirmed by ear; 2 follows from the same geometry.

This supersedes the two-Magisk-module approach floating around for this tablet (one module per orientation), since it handles both orientations in one place and needs no Magisk.

Adds a small service (phh-spkrot) that points all four CS35L41 amps at the
stereo slots so every speaker plays, and reassigns L/R per display rotation
so imaging stays correct in every orientation. Runs in phhsu_daemon; no-ops
on hardware without these controls. Verified on Galaxy Tab S6 (SM-T860).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@phhusson

phhusson commented Jul 26, 2026

Copy link
Copy Markdown

Well, that's a no. the while true sleep 0.5 really sucks. Calling dumpsys ain't great either.
Also not a fan of calling tinymix, but if nothing else works I can live with it.

I think the proper solution is to do some calls to AudioSystem.setParameters() from treble_app based on rotation events Android will send. You can find some already existing setParameters in https://github.com/TrebleDroid/treble_app/blob/master/app/src/main/java/me/phh/treble/app/Samsung.kt

To find those parameters, two ways:

  • read vendor/lib/hw/audio.primary or something like that. Usually a good ol' strings on it should help just to find what's the name of the parameter
  • decompile stock rom's system (I'd say services.jar? but grepping all jar/apk for setParameters can help find stuff) and you'll find the various setParameters

If you can't find anything the setParameters call, set a rotation property from treble app (let's say persist.sys.phh.audio_rotation) and call phh-spkrot.sh with the value of the rotation when the rotation changes

Replace the polling watcher with a one-shot triggered by
persist.sys.phh.audio_rotation (published by treble_app on rotation
change). No more busy loop or dumpsys; just re-maps the four amp slots
per rotation. Self-noops on other hardware.
@bod09

bod09 commented Jul 26, 2026

Copy link
Copy Markdown
Author

Thanks, that makes sense. I have reworked it along the lines you described.

The busy loop and dumpsys are gone. It is now event driven: treble_app registers a DisplayListener and publishes the display rotation into persist.sys.phh.audio_rotation, and this PR's phh-spkrot.sh is now a one-shot triggered by that property (start on property:persist.sys.phh.audio_rotation=*). It just sets the four amp slot controls once per rotation change and exits. The amp routing persists across audio streams, so no re-assert loop is needed.

treble_app side: TrebleDroid/treble_app#50

On setParameters: I did look. The HAL does receive the rotation already (sec_set_parameters parses rotation=N), but for these speakers it only ever feeds the SoundBooster DSP path (sec_set_soundbooster_rotation writing an "SB rotation" control), which is not active under a GSI, so nothing re-maps the actual amp routing. I could not find any setParameters that moves the four CS35L41 amps, which is why it falls back to setting the ASPRX1 slot controls via tinymix. Still using tinymix as you said you could live with.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants