Identity
| Part | Sensortek STK3311 (ambient light + proximity sensor with on-chip IR LED driver) |
| Role | Screen-blank / brightness input near the earpiece |
| Bus / address | i2c3 addr 0x48; chip ID = 0x12 |
| GPIO / IRQ | INT on GPIO4_D3, edge-falling (DTS: interrupts = <RK_PD3 IRQ_TYPE_EDGE_FALLING>) — wired |
| Datasheet | Sensortek STK3311 product page |
| Pine64 wiki | PinePhone Pro — Sensors |
| Schematic | sheet 11 (sensor cluster, PS LED, earpiece area) |
Status — ● working
Driver attaches, reports chip ID 0x12, and exposes raw ALS / PS /
near sysctls under dev.stk3311.0.*. On the bench it reads
proximity raw samples in the 78–80 range with near=0 when nothing’s
covering the sensor. The PS near/far transition IRQ is wired via
the chip’s STK_REG_INT.PS_NF mode with thresholds at 1500 (high) and
800 (low) raw counts; the taskqueue worker reads + clears FLAG,
caches near plus proximity_raw / illuminance_raw. Verified live
on 2026-05-03: irq_active=1, flags=193 (FLAG_PS_NF | FLAG_PS_DR),
enable=1, IRQ count tracking real near/far transitions during the
bench predicate sweep.
What remains for userspace integration
The driver is fully working at the kernel level; everything below is how the proximity / ambient-light signal turns into useful behaviour for the user:
- In-call screen blanking is the canonical proximity use case.
Shipped userland is
overlay/usr/local/bin/phone-proximity(10 Hzdev.stk3311.0.nearpoller →swaymsg "output * power off"/hyprctl dispatch dpms offwith 200 ms debounce). Wired as commented-outexec_always/exec-oncein the Sway and Hyprland configs — opt in once a real call path exists to test it against. - ALS-driven backlight scaling is unwritten. The raw illuminance
value would need a curve fit per device cover-glass, then a small
daemon that calls
backlight(8)on slow exponential averaging. - Threshold tuning.
THDH_PS = 1500/THDL_PS = 800were chosen off the bench-untested guess range. Real earpiece exposure (cheek against the speaker grille) might need different values.
Driver
- Our tree:
src/sys/dev/iicbus/stk3311.c(~300 lines) — minimaliicbusdriver, polled sysctl interface, near-threshold computed in software. - Linux mainline:
drivers/iio/light/stk3310.c— the STK3311 uses the same driver as its STK3310 sibling; full IIO interface with interrupt thresholds.
The STK3311 has an on-chip near/far hysteresis comparator that can
raise an interrupt only on threshold crossings, which is what the
Linux IIO driver uses. The local driver now programs those threshold
registers and updates cached near state from the taskqueue worker,
so userland can poll the sysctl without causing continuous I2C reads.
Open work
Wire— done onINTas an interruptGPIO4_D3edge-falling, samerk_i2cchild IRQ pattern as Goodix (commit2972362).Program the chip’s threshold registers and let the IRQ do the wakeup work.— done;THDH_PS = 1500,THDL_PS = 800raw counts. Tune from real earpiece exposures.Connect— first cut:near=1to a screen-blank daemon (or omfreebdy’s opt-in monitor) for in-call screen blanking.overlay/usr/local/bin/phone-proximitypollsdev.stk3311.0.nearat 10 Hz, debounces 200 ms, and callsswaymsg "output * power off"/hyprctl dispatch dpms off. Wired as commented-outexec_always/exec-oncein the Sway and Hyprland configs; opt in after the bench predicate below.- Tune the ALS reading curve so it can drive backlight scaling.
Parity verification
mise run debug:sensors:phone captures this receipt together with the
MPU-6500, magnetometer, TSADC, and eFuse sysctls.
- Pre-fix (polling, 2026-04-29):
sysctl dev.stk3311.0.neardid a synchronous I2C read ofFLAGon every sysctl call. A polling daemon readingnearat 10 Hz to drive screen blanking would have generated ~10 i2c3 transactions/sec.top -SH | grep stkshowed nothing — the work happened in whatever process polled. - Post-fix (IRQ-driven, 2026-04-30):
sysctl dev.stk3311.0.irq_activeis1after attach.sysctl dev.stk3311.0.nearflips from0to1within ~100 ms of covering the sensor with a finger; back to0within ~100 ms of uncovering. Latency dominated by the taskqueue thread wakeup, not by the chip.sysctl dev.stk3311.0.irq_countincrements only on near→far / far→near transitions, not continuously.- Idle: zero I2C traffic to address
0x48between transitions.
- Falsifier — IRQ never fires:
irq_countstays at 0 even with finger taps. Check dmesg forIRQ-driven near/far updates; absent meansbus_alloc_resource_any(SYS_RES_IRQ, …)returned NULL (rk_i2c bus_* method passthrough not applied) or the chip’sSTK_REG_INT.PS_NFwrite didn’t stick. - Falsifier — IRQ storm:
irq_countclimbs into the thousands per second. Means the worker isn’t actually clearingFLAG.PSINT(W1C semantics: write the bit back to clear), or the thresholds collapse into each other and the chip oscillates. DefaultTHDH=1500,THDL=800provides sane hysteresis; lowering either too far re-introduces oscillation. - Proximity script bench predicate: with
phone-proximityrunning under the user’s Sway / Hyprland session, cover the proximity sensor (top of the earpiece) with a finger. The screen blanks within ~300 ms (10 Hz poll + 200 ms debounce). Uncovering wakes it within the same window.--dry-runlogsnear 0->1; blanking/near 1->0; wakingto stderr without driving the compositor. - Falsifier — no blank: if
dev.stk3311.0.nearis stuck at0even with the sensor covered, the kernel IRQ path didn’t fire (see theirq_countpredicate above);phone-proximity --dry-runconfirms by never logging a transition. Ifneardoes flip but the screen stays on,swaymsg/hyprctlaren’t reaching the compositor — checkSWAYSOCK/HYPRLAND_INSTANCE_SIGNATUREin the script’s environment, and confirm the active output’s name (the script usesoutput */DSI-1).
Related
- Where we are — current sensor status.
- Hardware reference — chip manifest.