Files
bfe-core1106-sdk/kernel/rv1106-enablement/audio/PORT-PROGRESS.md
BFE Engineering 42fb386f60 docs: ASCII typography and style normalization across all repo text
Replace every em dash with real punctuation (rewrites, not hyphen swaps)
in docs, code comments, scripts, configs, and the port records; convert
en dashes, curly quotes, ellipsis glyphs, arrows, and section signs to
ASCII; drop machine-writing tell phrases from living docs. ADR titles
now use a colon. The M2 bring-up DTS model string carried an em dash
into the patch series and its record echoes; fixed at both, and the full
series re-verified to apply cleanly onto pristine 6.18.46. One comment
in freshness.h deliberately names the em dash glyph the UI renders as
the unknown mark; that is data, kept as prose naming it.

Verified: cargo tests (sim, config-lint, rs485-bridge), shellcheck,
both driver MC/DC harnesses, patches-apply.
2026-08-31 12:31:27 -06:00

231 lines
14 KiB
Markdown

# RV1106 Audio Codec: 5.10 (Rockchip BSP) -> 6.18.46 Port
Status: **zImage + rockchip/rv1106-warden.dtb build cleanly, 0 errors, 0 warnings**, with
`SND_SOC_RV1106=y` (acodec) and `SND_SOC_RK_DSM=y` (dsm) both built in, and the
`simple-audio-card` sound card wired in the board DT. Verified against `System.map` (host
`grep`, not cross-`nm`: see Verification below). **Not flashed or run on hardware**: this is
a build-only port; on-target audio verification is explicitly deferred to the parent
session / bench.
Trees involved:
- Target (edited): `<flare-edge>/research/linux-6.18.46/`
- Vendor source (read-only, copy-from): `<flare-edge>/sdk/sysdrv/source/kernel/` (Rockchip 5.10.160)
## Audio path
`i2s0_8ch` (DAI, already built/enabled pre-port) -> `acodec` (analog codec, this port) ->
speaker/headphone, tied together by a mainline `simple-audio-card`. The digital speaker
modulator (`dsm`) was also ported and builds cleanly, but is left **disabled** in the board DT,
matching the vendor 86-Panel board file (`rv1106-luckfox-pico-86panel-ipc.dtsi`), which also
ships `dsm` and `dsm_sound` disabled: the panel's actual audio path is acodec-only.
## Files copied (verbatim from vendor, then patched in place)
From `sdk/sysdrv/source/kernel/sound/soc/codecs/` to the 6.18 tree's `sound/soc/codecs/`:
| File | Lines | Notes |
|---|---|---|
| `rv1106_codec.c` | 2317 | Analog codec (acodec) driver: the priority target |
| `rv1106_codec.h` | n/a | Register/bitfield definitions, copied unmodified |
| `rk_dsm.c` | 653 | Digital speaker modulator (DSM) driver |
| `rk_dsm.h` | n/a | Register/bitfield definitions, copied unmodified |
## Kconfig / Makefile wiring
`sound/soc/codecs/Kconfig`:
- Added `config SND_SOC_RK_DSM` (tristate, `depends on ARCH_ROCKCHIP || COMPILE_TEST`) after
`SND_SOC_RK817`, before `SND_SOC_RL6231`.
- Added `config SND_SOC_RV1106` (tristate, `depends on ARCH_ROCKCHIP || COMPILE_TEST`,
`select REGMAP_MMIO`) after `SND_SOC_RTQ9128`, before `SND_SOC_SDW_MOCKUP` (alphabetical
slot for "RV").
- Neither entry restricts to `ARM64` (unlike `SND_SOC_RK3308`'s existing
`depends on ARM64 || COMPILE_TEST` in this tree): RV1106 is Cortex-A7 / ARM32-only, so an
ARM64 dependency would make the symbol unselectable on this board's `ARCH=arm` build.
`sound/soc/codecs/Makefile` (6.18 uses `snd-soc-<name>-y := <objs>.o` instead of the vendor's
`snd-soc-<name>-objs := <objs>.o`, same semantics, current tree's naming convention followed):
- `snd-soc-rk-dsm-y := rk_dsm.o` + `obj-$(CONFIG_SND_SOC_RK_DSM) += snd-soc-rk-dsm.o`
- `snd-soc-rv1106-y := rv1106_codec.o` + `obj-$(CONFIG_SND_SOC_RV1106) += snd-soc-rv1106.o`
## Config symbols set
Via `./scripts/config --enable <SYMBOL>` then `make ARCH=arm olddefconfig`, confirmed `=y` in
`.config` afterwards (no silent Kconfig-dependency drops):
```
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_RV1106=y
CONFIG_SND_SOC_RK_DSM=y
CONFIG_SND_SIMPLE_CARD=y
CONFIG_SND_SIMPLE_CARD_UTILS=y
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y (already y: DMA glue for the DAI)
```
Note: `SOUND`/`SND`/`SND_SOC` were `=m` in the pre-port `.config` (the whole ALSA/ASoC
subsystem was module-only). They had to be flipped to `=y` first: a tristate symbol can't be
`y` while its dependency chain is capped at `m`; otherwise `SND_SOC_RV1106=y` would silently
clamp to `m` under `olddefconfig`, and `System.map` (built-in symbols only) would show nothing
even with `grep -c rv1106_codec` returning 0, which is exactly the silent-drop failure mode the
task asked to guard against. `REGMAP_MMIO` and `MFD_SYSCON` were already `=y` (pulled in by
other on-SoC drivers), so no additional dependency chasing was needed for those.
## API-delta fixes (5.10 BSP -> 6.18 mainline ASoC)
The vendor 5.10.160 BSP already tracks a fairly modern ASoC callback shape (component-based
`.probe`/`.remove`, `snd_soc_dai_ops` with `.mute_stream(dai, mute, stream)`, etc.), so the
actual delta surface was small. Six fixes total, all found by iterating single-object builds
(`make ... sound/soc/codecs/rv1106_codec.o`) before doing the full `zImage` build:
| # | File | Change |
|---|---|---|
| 1 | `rv1106_codec.c` | Dropped `#include <linux/rockchip/grf.h>`: vendor-BSP-only header, not present in mainline. Nothing in the file actually needs symbols from it: `PERI_GRF_PERI_CON1` is defined locally in the driver and the GRF is accessed generically via `syscon_regmap_lookup_by_phandle()` + `regmap_write()`. Documented in a comment at the top of the include block. |
| 2 | `rv1106_codec.c` | Replaced `#include <linux/of_gpio.h>` with `#include <linux/gpio/consumer.h>`. The driver calls `devm_gpiod_get_optional()` / `gpiod_direction_output()` (the `gpiod_*` consumer API), which live in `gpio/consumer.h`, not `of_gpio.h` (legacy integer-GPIO header): this only happened to resolve on 5.10 via a transitive include chain that no longer holds in 6.18. |
| 3 | `rv1106_codec.c` | Added `#include <linux/of_device.h>`: `of_match_device()` is declared there, not pulled in transitively by `of_platform.h` any more; without it the call was an implicit-function-declaration error (`-Werror`) plus a pointer-from-int warning on the assignment. |
| 4 | `rv1106_codec.c` | `snd_soc_get_volsw_range()` / `snd_soc_put_volsw_range()` (used in `rv1106_codec_mic_gain_get/put` and `rv1106_codec_hpmix_gain_get/put`) no longer exist as separate mainline entry points: they were folded away when `SOC_SINGLE_RANGE`/`SOC_SINGLE_RANGE_TLV` were made simple aliases over `SOC_SINGLE_VALUE`. The unified `snd_soc_get_volsw()`/`snd_soc_put_volsw()` already honour the `xmin` field of `struct soc_mixer_control`, so they're a drop-in replacement; confirmed by checking how the affected controls are declared (`SOC_SINGLE_EXT_TLV`, min=0): behavior is identical. 4 call sites fixed. |
| 5 | `rv1106_codec.c` | `SND_SOC_DAIFMT_CBS_CFS` -> `SND_SOC_DAIFMT_CBC_CFC`, `SND_SOC_DAIFMT_CBM_CFM` -> `SND_SOC_DAIFMT_CBP_CFP` in `rv1106_set_dai_fmt()`'s clock-role switch. Mainline renamed the old master/slave (`CBM`/`CBS`) constants to the consumer/provider naming (`CBC`/`CBP`) some releases back; `SND_SOC_DAIFMT_MASTER_MASK` itself still exists as a compat alias for `CLOCK_PROVIDER_MASK`, so only the two case labels needed renaming. Verified the semantic mapping against the branch bodies (codec-slave -> `IO_MODE_SLAVE`/`MODE_SLAVE` register bits = consumer = `CBC_CFC`; codec-master -> `..._MASTER` bits = provider = `CBP_CFP`). |
| 6 | `rv1106_codec.c` + `rk_dsm.c` | `struct platform_driver.remove` is `void (*remove)(struct platform_device *)` in 6.18 (was `int (*remove)(struct platform_device *)`, transitional `.remove_new` before that). Changed `rv1106_platform_remove()` and `rk_dsm_platform_remove()` from `static int ... { ...; return 0; }` to `static void ...` (dropped the trailing `return 0;`). This was an `-Werror=incompatible-pointer-types` build error, not a warning. |
That's the complete fix list: nothing else needed touching. No vendor-only headers other than
`rockchip/grf.h` were pulled in; `linux/mfd/syscon.h`, `linux/reset.h`, `linux/clk.h`,
`linux/regulator/consumer.h`, `sound/pcm_params.h`, `sound/soc.h`, `sound/tlv.h`,
`sound/dmaengine_pcm.h` all exist unchanged in mainline and needed no stubbing.
## rk_dsm (digital speaker modulator) decision
**Kept in, not dropped.** It only needed fix #6 above (the same `platform_driver.remove` void
signature change as the acodec), a one-line-class fix, well under the "drop if it fights the
API a lot" threshold in the task brief. `SND_SOC_RK_DSM=y` is set and `rk_dsm.c` links cleanly
into `zImage` (17 `rk_dsm`-prefixed symbols in `System.map`, including
`rk_dsm_platform_remove`, `rk_dsm_hw_params`, `rk_dsm_set_dai_fmt`).
However, the DSM node (`dsm: codec-digital@ff340000`) and its companion `dsm_sound` machine
card are **not wired up** in `rv1106-warden.dts`: `&dsm { status = "disabled"; };` is set
explicitly, matching the vendor `rv1106-luckfox-pico-86panel-ipc.dtsi`, which itself ships `dsm`
disabled and `dsm_sound` as `status = "disabled"`. The 86-Panel's speaker/headphone path is the
acodec, not the DSM Class-D path; DSM is present on other RV1106 boards (bare speaker modules)
but not this one. The driver is built and available (a future DT change is all that's needed to
turn it on) but is not part of this board's active audio graph.
## Device tree changes (`arch/arm/boot/dts/rockchip/rv1106-warden.dts`)
`i2s0_8ch` was already `status = "okay"` pre-port (from an earlier "sweep" commit) and already
carries `#sound-dai-cells = <0>` in the base `rv1106.dtsi` node: no change needed there.
The base `rv1106.dtsi` `acodec: acodec@ff480000` node (mirrored from the vendor's own
`arch/arm/boot/dts/rv1106.dtsi`, which has the same gap) does **not** carry
`#sound-dai-cells`, unlike `i2s0_8ch` and `dsm`, which both already have it in the base dtsi.
The vendor board overlay (`rv1106-luckfox-pico-86panel-ipc.dtsi`) adds it via override, so this
port does the same, appended at the end of `rv1106-warden.dts` after the existing GMAC block:
```dts
&acodec {
#sound-dai-cells = <0>;
status = "okay";
};
&dsm {
status = "disabled";
};
/ {
acodec_sound: acodec-sound {
compatible = "simple-audio-card";
simple-audio-card,name = "rv1106-acodec";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <256>;
simple-audio-card,cpu {
sound-dai = <&i2s0_8ch>;
};
simple-audio-card,codec {
sound-dai = <&acodec>;
};
};
};
```
Verified against the decompiled built `.dtb` (`dtc -I dtb -O dts`):
- `acodec@ff480000`: `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x5a>`
- `i2s@ffae0000` (i2s0_8ch): `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x59>`
- `codec-digital@ff340000` (dsm): `status = "disabled"`
- `acodec-sound` node present with `sound-dai = <0x59>` (cpu) / `sound-dai = <0x5a>` (codec):
phandles correctly resolve to the i2s0_8ch/acodec nodes above.
## Build status
Exact commands run (per the task's build recipe):
```sh
export PATH="<flare-edge>/sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:/usr/bin:/bin"
export ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf-
cd <flare-edge>/research/linux-6.18.46
make ARCH=arm CROSS_COMPILE=$CROSS_COMPILE zImage rockchip/rv1106-warden.dtb -j"$(nproc)"
```
Result: **exit 0, 0 errors, 0 warnings** (a `grep -iE "error|warn"` over the full build log for
both the touched-file single-object builds and the final combined `zImage`+dtb build returned
nothing). `arch/arm/boot/zImage` and `arch/arm/boot/dts/rockchip/rv1106-warden.dtb` both
produced. XZ compression and the existing console/earlycon config were left untouched.
## Verification (build-only, see hardware watch-list below for what's deferred)
Per the task instructions, `System.map` (produced by the **host** toolchain's `nm`, ground
truth for built-in linkage, not the cross-`nm`, which mis-lists symbols) was checked directly
with `grep`:
```
$ grep -c rv1106_codec System.map
54
$ grep -c rk_dsm System.map
17
$ grep -c soc_codec_dev_rv1106 System.map
1
```
54 `rv1106_codec`-prefixed symbols and 17 `rk_dsm`-prefixed symbols are linked into the kernel
image: both drivers are genuinely built in (`=y`), not silently dropped to module or excluded.
`soc_codec_dev_rv1106` (the `snd_soc_component_driver` struct) is present, confirming the
component registration path is compiled in.
## Hardware-verify watch-list (deferred to the parent session / bench, NOT done here)
This session did not flash or touch any hardware, per the task boundary. When the parent
session verifies on a real 86-Panel:
- `aplay -l` should list a card named **`rv1106-acodec`** (from
`simple-audio-card,name = "rv1106-acodec"` in the DT): this is the ALSA card name to look
for, not a device path.
- Expect one playback + one capture PCM stream under that card (`rv1106-hifi` DAI: playback 1-2ch,
capture 1-4ch, 8kHz-192kHz, S16_LE/S20_3LE/S24_LE/S32_LE).
- `amixer -c <card> controls` should surface the vendor mixer controls carried over unchanged
from the BSP driver (ADC MIC Left/Right Gain, ADC ALC Left/Right Volume, HPF cutoff, DAC/lineout
gain, mic bias, etc.): these are pure regmap register pokes, unaffected by this port's API-shim
fixes, so if the card enumerates at all they should already work correctly.
- Watch dmesg for probe-order issues: `acodec` depends on `PCLK_ACODEC`, `MCLK_ACODEC_TX`,
`MCLK_I2S0_8CH_TX` clocks and `SRST_P_ACODEC` reset all resolving before probe; a probe
deferral (`-EPROBE_DEFER`) once at boot before the CRU driver is up is normal, a permanent
failure is not.
- If `aplay -l` shows no card at all: check `dmesg` for `simple-audio-card` DAI-link binding
failures first (a `#sound-dai-cells` mismatch would show as
"ASoC: ... error getting cpu/codec dai info" at parse time) before suspecting the codec driver
itself, since the DT wiring is the newest/least-proven part of this port.
- The actual audible test (does sound come out of the speaker/headphone jack) is explicitly
**deferred to the bench**: this session only proves the driver builds, links, and the DT
graph resolves; it does not and cannot prove the analog output path works on real silicon.
---
## Parent-session verify + fix (2026-08-25)
**Gap found:** the subagent set `SND_SOC_RV1106=y` + `SND_SIMPLE_CARD=y` but left
the **cpu DAI** `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=m` (module). On the bare-kernel
_b boot no modules load, so i2s0_8ch never registered a DAI -> the simple-audio-card
stuck in `deferred probe pending: asoc-simple-card: parse error`, no card.
**Fix:** `CONFIG_SND_SOC_ROCKCHIP=y` + `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y` (the
i2s0_8ch node already carries the `rockchip,rv1126-i2s-tdm` fallback compatible +
`#sound-dai-cells=<0>`, so the mainline driver binds it).
**VERIFIED on c8a3:** `/proc/asound/cards` -> `0 [rv1106acodec]: simple-card -
rv1106-acodec`; `aplay -l` -> `card 0: rv1106acodec, device 0:
ffae0000.i2s-rv1106-hifi`; `/dev/snd/` has `controlC0 pcmC0D0p pcmC0D0c` (playback
+ capture). Audible speaker test deferred to the bench (with the display).