From 55e8d46fc655bb44ebb72000086c5bcc33c5ec68 Mon Sep 17 00:00:00 2001 From: Trey Moen Date: Sun, 5 Jul 2026 10:28:15 -0700 Subject: [PATCH 1/3] sound: bring up the mici audio card on the mainline kernel Wire up the comma four (mici) audio card end to end on vamOS: kernel config, device tree, ADSP firmware, driver fixes, and the runit userspace init. Kernel: - vamos.config: enable the QDSP6 audio stack (APR, PD mapper, Q6V5 PAS, SoundWire, SND_SOC_QDSP6/SDM845/SPDIF). - sdm845-comma-mici.dts: describe the sound card. adsp_pas is enabled with firmware-name qcom/sdm845/adsp.mbn; q6afedai/q6asmdai declare Secondary MI2S RX/TX and the MultiMedia1 (RX) / MultiMedia2 (TX) frontends; the qcom sdm845-sndcard links MM1/MM2 to spdif-dit/dir dummy codecs over sec_mi2s. - patches 0013-0015: guard NULL dai ops in snd_soc_dai_set/get_stream (the spdif dummy codecs register without an ops struct); constrain q6asm audio buffer IOVAs to the ADSP shared-mem aperture the downstream AVS 2.8 firmware validates against; track DSP stream open state across prepare failures so a failed prepare doesn't leak the stream for the rest of the boot. Firmware: - ship the factory ADSP PAS firmware (adsp.mdt + adsp.b00..b13) pulled from the device, tracked in LFS; Dockerfile symlinks adsp.mbn -> adsp.mdt so the q6v5-pas loader finds the MDT header and loads the segments alongside it. Userspace: - sound_init.sh: the ADSP auto-boots via q6v5-pas, so just wait for the commamici ASoC card, fix /dev/snd perms (no udev rule fires), and route MultiMedia1->SEC_MI2S_RX (playback hw:0,0) + MultiMedia2->SEC_MI2S_TX (capture hw:0,1). Bail cleanly if no card appears (e.g. tizi). - asound.conf: card-id-gated asym default so PortAudio opens playback on hw:0,0 and capture on hw:0,1. - drop the downstream adsp/cdsp remoteproc services, adsp-start.sh, and amplifier.py; init.qcom.sh no longer pokes the downstream subsys restart knobs. --- .gitattributes | 2 + kernel/configs/vamos.config | 14 ++ kernel/dts/sdm845-comma-mici.dts | 102 +++++++++++ kernel/firmware/qcom/sdm845/adsp.b00 | 3 + kernel/firmware/qcom/sdm845/adsp.b01 | 3 + kernel/firmware/qcom/sdm845/adsp.b02 | 3 + kernel/firmware/qcom/sdm845/adsp.b03 | 3 + kernel/firmware/qcom/sdm845/adsp.b04 | 3 + kernel/firmware/qcom/sdm845/adsp.b05 | 3 + kernel/firmware/qcom/sdm845/adsp.b06 | 3 + kernel/firmware/qcom/sdm845/adsp.b07 | 3 + kernel/firmware/qcom/sdm845/adsp.b08 | 3 + kernel/firmware/qcom/sdm845/adsp.b09 | 3 + kernel/firmware/qcom/sdm845/adsp.b10 | 3 + kernel/firmware/qcom/sdm845/adsp.b11 | 3 + kernel/firmware/qcom/sdm845/adsp.b12 | 3 + kernel/firmware/qcom/sdm845/adsp.b13 | 3 + kernel/firmware/qcom/sdm845/adsp.mdt | 3 + ...rd-NULL-dai-ops-in-set_stream-get_st.patch | 44 +++++ ...-dai-constrain-audio-buffer-IOVAs-to.patch | 50 ++++++ ...-dai-track-DSP-stream-open-state-acr.patch | 48 ++++++ tools/build/Dockerfile | 3 +- userspace/root/etc/asound.conf | 29 ++++ userspace/root/etc/initscripts/adsp.sh | 35 ---- userspace/root/etc/sv/adsp/finish | 3 - userspace/root/etc/sv/adsp/run | 9 - userspace/root/etc/sv/cdsp/finish | 3 - userspace/root/etc/sv/cdsp/run | 9 - userspace/root/usr/bin/cdsp.sh | 16 -- userspace/root/usr/comma/init.qcom.sh | 5 - userspace/root/usr/comma/sound/adsp-start.sh | 68 -------- userspace/root/usr/comma/sound/amplifier.py | 161 ------------------ userspace/root/usr/comma/sound/sound_init.sh | 29 ++-- .../root/usr/local/qr-linux/adsp-start.sh | 67 -------- userspace/uv/pyproject.toml | 2 +- 35 files changed, 352 insertions(+), 392 deletions(-) create mode 100644 kernel/firmware/qcom/sdm845/adsp.b00 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b01 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b02 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b03 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b04 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b05 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b06 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b07 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b08 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b09 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b10 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b11 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b12 create mode 100644 kernel/firmware/qcom/sdm845/adsp.b13 create mode 100644 kernel/firmware/qcom/sdm845/adsp.mdt create mode 100644 kernel/patches/0013-ASoC-soc-dai-guard-NULL-dai-ops-in-set_stream-get_st.patch create mode 100644 kernel/patches/0014-ASoC-qdsp6-q6asm-dai-constrain-audio-buffer-IOVAs-to.patch create mode 100644 kernel/patches/0015-ASoC-qdsp6-q6asm-dai-track-DSP-stream-open-state-acr.patch create mode 100644 userspace/root/etc/asound.conf delete mode 100755 userspace/root/etc/initscripts/adsp.sh delete mode 100755 userspace/root/etc/sv/adsp/finish delete mode 100755 userspace/root/etc/sv/adsp/run delete mode 100755 userspace/root/etc/sv/cdsp/finish delete mode 100755 userspace/root/etc/sv/cdsp/run delete mode 100755 userspace/root/usr/bin/cdsp.sh delete mode 100755 userspace/root/usr/comma/sound/adsp-start.sh delete mode 100755 userspace/root/usr/comma/sound/amplifier.py delete mode 100755 userspace/root/usr/local/qr-linux/adsp-start.sh diff --git a/.gitattributes b/.gitattributes index f339a1ab..3812ab7e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,5 @@ userspace/root/usr/share/fonts/*.woff2 filter=lfs diff=lfs merge=lfs -text kernel/firmware/**/*.mbn filter=lfs diff=lfs merge=lfs -text kernel/firmware/**/*.bin filter=lfs diff=lfs merge=lfs -text kernel/firmware/**/*.tlv filter=lfs diff=lfs merge=lfs -text +kernel/firmware/**/*.mdt filter=lfs diff=lfs merge=lfs -text +kernel/firmware/**/*.b[0-9][0-9] filter=lfs diff=lfs merge=lfs -text diff --git a/kernel/configs/vamos.config b/kernel/configs/vamos.config index 6c8e5e56..287561a7 100644 --- a/kernel/configs/vamos.config +++ b/kernel/configs/vamos.config @@ -64,6 +64,20 @@ CONFIG_QCOM_PDR_HELPERS=y CONFIG_RPMSG_CHAR=y CONFIG_RESET_QCOM_PDC=y +# Audio (ADSP QDSP6 -> Secondary MI2S) +CONFIG_QCOM_APR=y +CONFIG_QCOM_PD_MAPPER=y +CONFIG_QCOM_Q6V5_PAS=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_QCOM=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_QCOM=y +CONFIG_SND_SOC_QDSP6=y +CONFIG_SND_SOC_SDM845=y +CONFIG_SND_SOC_SPDIF=y + # PPP (for USB LTE modem data connection) CONFIG_PPP=y CONFIG_PPP_ASYNC=y diff --git a/kernel/dts/sdm845-comma-mici.dts b/kernel/dts/sdm845-comma-mici.dts index 396f6ef0..34b8b266 100644 --- a/kernel/dts/sdm845-comma-mici.dts +++ b/kernel/dts/sdm845-comma-mici.dts @@ -1,5 +1,7 @@ /dts-v1/; +#include +#include #include "sdm845-comma-common.dtsi" / { @@ -12,6 +14,22 @@ aliases { hsuart0 = &uart3; }; + + spkr_codec: audio-codec-0 { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + mic_codec: audio-codec-1 { + compatible = "linux,spdif-dir"; + #sound-dai-cells = <0>; + }; +}; + +&adsp_pas { + firmware-name = "qcom/sdm845/adsp.mbn"; + + status = "okay"; }; &i2c5 { @@ -56,6 +74,90 @@ data-lanes = <0>; }; +&q6afedai { + dai@18 { + reg = ; + qcom,sd-lines = <1>; + }; + + dai@19 { + reg = ; + qcom,sd-lines = <0>; + }; +}; + +&q6asmdai { + dai@0 { + reg = ; + direction = ; + }; + + dai@1 { + reg = ; + direction = ; + }; +}; + +&sound { + compatible = "qcom,sdm845-sndcard"; + pinctrl-0 = <&sec_mi2s_active>; + pinctrl-names = "default"; + model = "comma-mici"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + mm2-dai-link { + link-name = "MultiMedia2"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; + }; + }; + + speaker-dai-link { + link-name = "Secondary MI2S Playback"; + cpu { + sound-dai = <&q6afedai SECONDARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&spkr_codec>; + }; + }; + + mic-dai-link { + link-name = "Secondary MI2S Capture"; + cpu { + sound-dai = <&q6afedai SECONDARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&mic_codec>; + }; + }; +}; + +&tlmm { + sec_mi2s_active: sec-mi2s-active-state { + pins = "gpio80", "gpio81", "gpio82", "gpio83"; + function = "sec_mi2s"; + drive-strength = <8>; + bias-disable; + }; +}; + &uart3 { status = "okay"; }; diff --git a/kernel/firmware/qcom/sdm845/adsp.b00 b/kernel/firmware/qcom/sdm845/adsp.b00 new file mode 100644 index 00000000..623f4f6b --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b00 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2245f8f43316193eb38c1c679edaa30bce8c8694af595de2134e3547e1c7604 +size 532 diff --git a/kernel/firmware/qcom/sdm845/adsp.b01 b/kernel/firmware/qcom/sdm845/adsp.b01 new file mode 100644 index 00000000..5b46686e --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b01 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac49d1bd375f8ceabbcc6654c5f3f9ba0b5b4dcc4844d0d6cdd7e53d2e48849c +size 6920 diff --git a/kernel/firmware/qcom/sdm845/adsp.b02 b/kernel/firmware/qcom/sdm845/adsp.b02 new file mode 100644 index 00000000..632dbbce --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b02 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cab05eabe09d3a2d498af77dacc47d4f87b9d2018d545129393de7eb4e02e3c +size 8408 diff --git a/kernel/firmware/qcom/sdm845/adsp.b03 b/kernel/firmware/qcom/sdm845/adsp.b03 new file mode 100644 index 00000000..e55397ce --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b03 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7621dab757b12edbf13e9c94694337574c4cbe7951e93f79e7aba4727a75acb +size 120356 diff --git a/kernel/firmware/qcom/sdm845/adsp.b04 b/kernel/firmware/qcom/sdm845/adsp.b04 new file mode 100644 index 00000000..865c0f10 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b04 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54e8413c76006da3935a152d576aff87d6d789ab23a177c3d2ff6ace061e1050 +size 1831840 diff --git a/kernel/firmware/qcom/sdm845/adsp.b05 b/kernel/firmware/qcom/sdm845/adsp.b05 new file mode 100644 index 00000000..beedbbab --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b05 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ecf1dbb926cf4e9942aa54a5618fb2e5c2ca18fbf7f6c1c96c6c9794b3b0ab0 +size 1144252 diff --git a/kernel/firmware/qcom/sdm845/adsp.b06 b/kernel/firmware/qcom/sdm845/adsp.b06 new file mode 100644 index 00000000..ae56e1a5 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b06 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbe66973e4df0821d7b41ac185f1320f9624c3d83cdb50abe58896ab14b4cda8 +size 2872 diff --git a/kernel/firmware/qcom/sdm845/adsp.b07 b/kernel/firmware/qcom/sdm845/adsp.b07 new file mode 100644 index 00000000..3fc81e23 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b07 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b155f19ece77fb1724adc2d6dc8b7cf656c6797186d53d44c84dd6488130d4ee +size 33548 diff --git a/kernel/firmware/qcom/sdm845/adsp.b08 b/kernel/firmware/qcom/sdm845/adsp.b08 new file mode 100644 index 00000000..bb5d3608 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b08 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8383c669324c10f7ed67ba87edd62293f41deb27040ae581f2a286213eaadf17 +size 307832 diff --git a/kernel/firmware/qcom/sdm845/adsp.b09 b/kernel/firmware/qcom/sdm845/adsp.b09 new file mode 100644 index 00000000..7dd1ab9f --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b09 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0dae346c419a033edb26b33d8c34a04bfc277aef226bd5c405d2c1bf3dcd95a3 +size 100 diff --git a/kernel/firmware/qcom/sdm845/adsp.b10 b/kernel/firmware/qcom/sdm845/adsp.b10 new file mode 100644 index 00000000..37e88510 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b10 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec85c973d57a550181e1ffec7c86bee11490e26eba019b054ce0e052d47d3e2a +size 6474588 diff --git a/kernel/firmware/qcom/sdm845/adsp.b11 b/kernel/firmware/qcom/sdm845/adsp.b11 new file mode 100644 index 00000000..12b58a27 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b11 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e135d176faa9c5ae73f21a39e69979910a64f0ef666386766e4a7fc8e5da753 +size 308688 diff --git a/kernel/firmware/qcom/sdm845/adsp.b12 b/kernel/firmware/qcom/sdm845/adsp.b12 new file mode 100644 index 00000000..ab320eef --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b12 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cfc03d3957ada04b7c97b1619adc2ea21c19fd4ba76171f5a2fd82c061f3727 +size 2368 diff --git a/kernel/firmware/qcom/sdm845/adsp.b13 b/kernel/firmware/qcom/sdm845/adsp.b13 new file mode 100644 index 00000000..b61abca2 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.b13 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e58ab0261e0b8a6e6506f06508f5c015f701a52135ce1d0209e90ad6228b9b7e +size 143696 diff --git a/kernel/firmware/qcom/sdm845/adsp.mdt b/kernel/firmware/qcom/sdm845/adsp.mdt new file mode 100644 index 00000000..eec635d5 --- /dev/null +++ b/kernel/firmware/qcom/sdm845/adsp.mdt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2f69891f57e4f982fae331c272c682c419b00beee500bf166f96d6a89bd6e9f +size 7452 diff --git a/kernel/patches/0013-ASoC-soc-dai-guard-NULL-dai-ops-in-set_stream-get_st.patch b/kernel/patches/0013-ASoC-soc-dai-guard-NULL-dai-ops-in-set_stream-get_st.patch new file mode 100644 index 00000000..bba7bd47 --- /dev/null +++ b/kernel/patches/0013-ASoC-soc-dai-guard-NULL-dai-ops-in-set_stream-get_st.patch @@ -0,0 +1,44 @@ +From: Trey Moen +Date: Fri, 3 Jul 2026 22:30:00 -0700 +Subject: [PATCH] ASoC: soc-dai: guard NULL dai ops in + snd_soc_dai_set_stream/get_stream + +DAI drivers are allowed to register without a snd_soc_dai_ops struct +(e.g. the linux,spdif-dit/dir dummy codecs), and every accessor in +soc-dai.c checks dai->driver->ops before dereferencing it. +snd_soc_dai_set_stream() and snd_soc_dai_get_stream() do not, so a +machine driver calling them on such a codec DAI oopses. This happens on +sdm845 cards where qcom_snd_sdw_startup() runs for every DAI link: an +MI2S backend using spdif-dit/dir as its codec crashes on PCM open with +a NULL dereference at offsetof(struct snd_soc_dai_ops, set_stream). + +Check ops for NULL and fall back to the existing -ENOTSUPP paths, +matching the convention used by the other DAI ops wrappers. +--- + include/sound/soc-dai.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h +index 224396927..aa78cea66 100644 +--- a/include/sound/soc-dai.h ++++ b/include/sound/soc-dai.h +@@ -559,7 +559,7 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) + static inline int snd_soc_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) + { +- if (dai->driver->ops->set_stream) ++ if (dai->driver->ops && dai->driver->ops->set_stream) + return dai->driver->ops->set_stream(dai, stream, direction); + else + return -ENOTSUPP; +@@ -579,7 +579,7 @@ static inline int snd_soc_dai_set_stream(struct snd_soc_dai *dai, + static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, + int direction) + { +- if (dai->driver->ops->get_stream) ++ if (dai->driver->ops && dai->driver->ops->get_stream) + return dai->driver->ops->get_stream(dai, direction); + else + return ERR_PTR(-ENOTSUPP); +-- +2.53.0 diff --git a/kernel/patches/0014-ASoC-qdsp6-q6asm-dai-constrain-audio-buffer-IOVAs-to.patch b/kernel/patches/0014-ASoC-qdsp6-q6asm-dai-constrain-audio-buffer-IOVAs-to.patch new file mode 100644 index 00000000..741d4f4f --- /dev/null +++ b/kernel/patches/0014-ASoC-qdsp6-q6asm-dai-constrain-audio-buffer-IOVAs-to.patch @@ -0,0 +1,50 @@ +From: Trey Moen +Date: Fri, 3 Jul 2026 23:30:00 -0700 +Subject: [PATCH] ASoC: qdsp6: q6asm-dai: constrain audio buffer IOVAs to the + ADSP shared-mem aperture + +The downstream msm-audio-ion driver maps all ASM shared-memory buffers +through the apps SMMU into a fixed IOVA window, MSM_AUDIO_ION_VA_START +0x10000000 with MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF, i.e. IOVAs never +exceed 29 bits. Downstream-era ADSP firmware (AVS 2.8, as shipped on +the comma four) validates the SID-indexed address of +ASM_CMD_SHARED_MEM_MAP_REGIONS against that pre-configured aperture +and NAKs the map when the address lies outside it. Mainline dma-iommu +allocates IOVAs top-down from the 32-bit DMA limit (~0xfffxxxxx), +which such firmware rejects, so every PCM prepare fails with +"Audio Start: Buffer Allocation failed rc = -22". + +Cap the q6asm-dai DMA mask at 29 bits when an SMMU is in use so the +top-down IOVA allocator hands out addresses just below 0x20000000, +inside the window the DSP accepts. Firmware that accepts full 32-bit +IOVAs is unaffected beyond the narrower allocation range, which is far +larger than any realistic set of concurrently mapped audio buffers. +--- + sound/soc/qcom/qdsp6/q6asm-dai.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c +index b616ce316..1520b7840 100644 +--- a/sound/soc/qcom/qdsp6/q6asm-dai.c ++++ b/sound/soc/qcom/qdsp6/q6asm-dai.c +@@ -1298,11 +1298,16 @@ static int q6asm_dai_probe(struct platform_device *pdev) + return -ENOMEM; + + rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); +- if (rc < 0) ++ if (rc < 0) { + pdata->sid = -1; +- else ++ } else { + pdata->sid = args.args[0] & SID_MASK_DEFAULT; + ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(29)); ++ if (rc) ++ return rc; ++ } ++ + dev_set_drvdata(dev, pdata); + + rc = of_q6asm_parse_dai_data(dev, pdata); +-- +2.53.0 diff --git a/kernel/patches/0015-ASoC-qdsp6-q6asm-dai-track-DSP-stream-open-state-acr.patch b/kernel/patches/0015-ASoC-qdsp6-q6asm-dai-track-DSP-stream-open-state-acr.patch new file mode 100644 index 00000000..2925ab9b --- /dev/null +++ b/kernel/patches/0015-ASoC-qdsp6-q6asm-dai-track-DSP-stream-open-state-acr.patch @@ -0,0 +1,48 @@ +From: Trey Moen +Date: Sat, 4 Jul 2026 00:30:00 -0700 +Subject: [PATCH] ASoC: qdsp6: q6asm-dai: track DSP stream open state across + prepare failures + +q6asm_dai_prepare() only sets prtd->state after the whole open +sequence, including the media format block command, has succeeded. If +any step after ASM_STREAM_CMD_OPEN_WRITE/READ fails, the function +returns with the DSP stream open but prtd->state still +Q6ASM_STREAM_IDLE. Both the re-prepare cleanup in q6asm_dai_prepare() +and q6asm_dai_close() gate CMD_CLOSE on prtd->state, so the stream is +never closed: every subsequent prepare in that boot re-sends +OPEN_WRITE_V3 on the same session/stream and the DSP NAKs it with +ADSP_EALREADY (0x9), permanently wedging the frontend until an ADSP +restart. The failure that started the chain is also only logged at +info level, so the wedge is all that shows up in dmesg. + +Mark the stream open (Q6ASM_STREAM_STOPPED) as soon as the open +command succeeds so re-prepare and close always issue CMD_CLOSE, and +raise the format block failure log to error severity. +--- + sound/soc/qcom/qdsp6/q6asm-dai.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c +index b616ce316..824af93d2 100644 +--- a/sound/soc/qcom/qdsp6/q6asm-dai.c ++++ b/sound/soc/qcom/qdsp6/q6asm-dai.c +@@ -268,6 +268,8 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, + goto open_err; + } + ++ prtd->state = Q6ASM_STREAM_STOPPED; ++ + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, substream->stream); +@@ -294,7 +296,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, + + } + if (ret < 0) +- dev_info(dev, "%s: CMD Format block failed\n", __func__); ++ dev_err(dev, "%s: CMD Format block failed\n", __func__); + else + prtd->state = Q6ASM_STREAM_RUNNING; + +-- +2.53.0 diff --git a/tools/build/Dockerfile b/tools/build/Dockerfile index e52e870e..39c604e7 100644 --- a/tools/build/Dockerfile +++ b/tools/build/Dockerfile @@ -71,6 +71,7 @@ COPY ./kernel/firmware/ath10k/WCN3990/hw1.0 /lib/firmware/ath10k/WCN3990/hw1.0 COPY ./kernel/firmware/qcom/sdm845 /lib/firmware/qcom/sdm845 COPY ./kernel/firmware/qca /lib/firmware/qca RUN ln -sf /lib/firmware/ath10k/WCN3990/hw1.0/wlanmdsp.mbn /lib/firmware/qcom/sdm845/wlanmdsp.mbn +RUN ln -sf /lib/firmware/qcom/sdm845/adsp.mdt /lib/firmware/qcom/sdm845/adsp.mbn # Root filesystem overlay — all userspace files at their final paths COPY ./userspace/root/ / @@ -87,7 +88,7 @@ RUN for svc in \ avahi-ssh-publish power_monitor power_drop_monitor \ screen_calibration serial-hostname \ sound varwatch busybox-ntpd \ - irsc_util leprop adsp cdsp \ + irsc_util leprop \ qrtr-ns rmtfs tqftpserv pd-mapper \ comma modemmanager bluetoothd magic \ ssh-param-watcher ncm-param-watcher dnsmasq \ diff --git a/userspace/root/etc/asound.conf b/userspace/root/etc/asound.conf new file mode 100644 index 00000000..700bb3cf --- /dev/null +++ b/userspace/root/etc/asound.conf @@ -0,0 +1,29 @@ +pcm.!default { + type empty + slave.pcm { + @func refer + name { + @func concat + strings [ + "pcm.comma_default_" + { + @func card_id + card 0 + } + ] + } + default { + type plug + slave.pcm { + type hw + card 0 + } + } + } +} + +pcm.comma_default_commamici { + type asym + playback.pcm "plughw:0,0" + capture.pcm "plughw:0,1" +} diff --git a/userspace/root/etc/initscripts/adsp.sh b/userspace/root/etc/initscripts/adsp.sh deleted file mode 100755 index c01f11ef..00000000 --- a/userspace/root/etc/initscripts/adsp.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -############################################################################### -# -# This script is used for System V init scripts to start adsp -# -# Copyright (c) 2012-2016 Qualcomm Technologies, Inc. -# All Rights Reserved. -# Confidential and Proprietary - Qualcomm Technologies, Inc. -# -############################################################################### - -set -e - -case "$1" in - start) - echo -n "Starting adsp: " - /usr/local/qr-linux/adsp-start.sh - echo "done" - ;; - stop) - echo -n "Stopping adsp: " - echo 0 > /sys/kernel/boot_adsp/boot - echo "done" - ;; - restart) - $0 stop - $0 start - ;; - *) - echo "Usage adsp.sh { start | stop | restart}" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/userspace/root/etc/sv/adsp/finish b/userspace/root/etc/sv/adsp/finish deleted file mode 100755 index fbb5e8d2..00000000 --- a/userspace/root/etc/sv/adsp/finish +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# Delay restart to avoid spamming console on failure -sleep 5 diff --git a/userspace/root/etc/sv/adsp/run b/userspace/root/etc/sv/adsp/run deleted file mode 100755 index b2081037..00000000 --- a/userspace/root/etc/sv/adsp/run +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# adsp - Audio DSP init (runs once) -exec 2>&1 - -# Run the init script -/etc/initscripts/adsp.sh start - -# Stay up so runit doesn't restart -exec sleep infinity diff --git a/userspace/root/etc/sv/cdsp/finish b/userspace/root/etc/sv/cdsp/finish deleted file mode 100755 index fbb5e8d2..00000000 --- a/userspace/root/etc/sv/cdsp/finish +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# Delay restart to avoid spamming console on failure -sleep 5 diff --git a/userspace/root/etc/sv/cdsp/run b/userspace/root/etc/sv/cdsp/run deleted file mode 100755 index 240a5e80..00000000 --- a/userspace/root/etc/sv/cdsp/run +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# cdsp - Compute DSP init (runs once) -exec 2>&1 - -# Run the init script -/usr/sbin/cdsp.sh - -# Stay up so runit doesn't restart -exec sleep infinity diff --git a/userspace/root/usr/bin/cdsp.sh b/userspace/root/usr/bin/cdsp.sh deleted file mode 100755 index 8d3f9e89..00000000 --- a/userspace/root/usr/bin/cdsp.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright (c) 2018 Qualcomm Technologies, Inc. -# All Rights Reserved. -# Confidential and Proprietary - Qualcomm Technologies, Inc. -# - -set -e -echo -n "Starting cdsp: " - -if [ -f /sys/kernel/boot_cdsp/boot ]; then - echo 1 > /sys/kernel/boot_cdsp/boot -else - echo "File not found! : /sys/kernel/boot_cdsp/boot" -fi - -exit 0 diff --git a/userspace/root/usr/comma/init.qcom.sh b/userspace/root/usr/comma/init.qcom.sh index ca6b4a70..67eaf16c 100755 --- a/userspace/root/usr/comma/init.qcom.sh +++ b/userspace/root/usr/comma/init.qcom.sh @@ -1,10 +1,5 @@ #!/bin/bash -# don't restart whole SoC on subsystem crash -for i in {0..7}; do - echo "related" | sudo tee /sys/bus/msm_subsys/devices/subsys${i}/restart_level -done - # use max freq to boot up quickly, then limit echo 1689600 | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq echo 1689600 | sudo tee /sys/devices/system/cpu/cpufreq/policy4/scaling_max_freq diff --git a/userspace/root/usr/comma/sound/adsp-start.sh b/userspace/root/usr/comma/sound/adsp-start.sh deleted file mode 100755 index 675dbf91..00000000 --- a/userspace/root/usr/comma/sound/adsp-start.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -############################################################################### -# -# This script is used for administration of the Hexagon DSP -# -# Copyright (c) 2012-2016 Qualcomm Technologies, Inc. -# All Rights Reserved. -# Confidential and Proprietary - Qualcomm Technologies, Inc. -# -############################################################################### - -KEEP_ALIVE=0 -subsys_name="" - -echo -n "/firmware/image" > /sys/module/firmware_class/parameters/path - -# Wait for adsp.mdt to show up -count=0 -while [ ! -s /firmware/image/adsp.mdt ]; do - sleep 0.1 - # wait 10s for /firmware mounted - count=$(( $count + 1 )) - if [ $count -ge 100 ]; then - echo "[ERROR] Can not find the adsp's firmware" - exit 1 - fi -done - -for subsys in `ls /sys/bus/msm_subsys/devices`; do - name=`cat /sys/bus/msm_subsys/devices/${subsys}/name` - if [ "`cat /sys/bus/msm_subsys/devices/${subsys}/name`" = "adsp" ]; then - subsys_name="${subsys}" - break - fi -done - -if [ "$KEEP_ALIVE" = "1" ]; then - if [ -n "${subsys_name}" ]; then - sysctl -w kernel.panic=0 - echo 1 > /sys/bus/msm_subsys/devices/${subsys_name}/keep_alive - else - echo "[ERROR] Can not keep adsp alive" - fi -fi - -# FIXME: See ATL-3054 -echo 1 > /sys/module/subsystem_restart/parameters/enable_debug -# Bring adsp out of reset -echo "[INFO] Bringing adsp out of reset" -echo "${subsys_name}" -echo 1 > /sys/kernel/boot_adsp/boot - -# wait boot finished -if [ -n "${subsys_name}" ]; then - count=0 - state=`cat /sys/bus/msm_subsys/devices/${subsys_name}/state` - while [ "${state}" != "ONLINE" ]; do - # wait 2s for subsys boot finished - count=$(( $count + 1 )) - if [ $count -ge 200 ]; then - echo "[ERROR] adsp fail to boot" - exit 1 - fi - state=`cat /sys/bus/msm_subsys/devices/${subsys_name}/state` - sleep 0.1 - done -fi - diff --git a/userspace/root/usr/comma/sound/amplifier.py b/userspace/root/usr/comma/sound/amplifier.py deleted file mode 100755 index dbc79945..00000000 --- a/userspace/root/usr/comma/sound/amplifier.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python3 -import time -from smbus2 import SMBus -from collections import namedtuple -from typing import List - -# https://datasheets.maximintegrated.com/en/ds/MAX98089.pdf - -AmpConfig = namedtuple('AmpConfig', ['name', 'value', 'register', 'offset', 'mask']) -EQParams = namedtuple('EQParams', ['K', 'k1', 'k2', 'c1', 'c2']) - -def configs_from_eq_params(base, eq_params): - return [ - AmpConfig("K (high)", (eq_params.K >> 8), base, 0, 0xFF), - AmpConfig("K (low)", (eq_params.K & 0xFF), base + 1, 0, 0xFF), - AmpConfig("k1 (high)", (eq_params.k1 >> 8), base + 2, 0, 0xFF), - AmpConfig("k1 (low)", (eq_params.k1 & 0xFF), base + 3, 0, 0xFF), - AmpConfig("k2 (high)", (eq_params.k2 >> 8), base + 4, 0, 0xFF), - AmpConfig("k2 (low)", (eq_params.k2 & 0xFF), base + 5, 0, 0xFF), - AmpConfig("c1 (high)", (eq_params.c1 >> 8), base + 6, 0, 0xFF), - AmpConfig("c1 (low)", (eq_params.c1 & 0xFF), base + 7, 0, 0xFF), - AmpConfig("c2 (high)", (eq_params.c2 >> 8), base + 8, 0, 0xFF), - AmpConfig("c2 (low)", (eq_params.c2 & 0xFF), base + 9, 0, 0xFF), - ] - -BASE_CONFIG = [ - AmpConfig("MCLK prescaler", 0b01, 0x10, 4, 0b00110000), - AmpConfig("PM: enable speakers", 0b11, 0x4D, 4, 0b00110000), - AmpConfig("PM: enable DACs", 0b11, 0x4D, 0, 0b00000011), - AmpConfig("Enable PLL1", 0b1, 0x12, 7, 0b10000000), - AmpConfig("Enable PLL2", 0b1, 0x1A, 7, 0b10000000), - AmpConfig("DAI1: I2S mode", 0b00100, 0x14, 2, 0b01111100), - AmpConfig("DAI2: I2S mode", 0b00100, 0x1C, 2, 0b01111100), - AmpConfig("DAI1 Passband filtering: music mode", 0b1, 0x18, 7, 0b10000000), - AmpConfig("DAI1 voice mode gain (DV1G)", 0b00, 0x2F, 4, 0b00110000), - AmpConfig("DAI1 attenuation (DV1)", 0x0, 0x2F, 0, 0b00001111), - AmpConfig("DAI2 attenuation (DV2)", 0x0, 0x31, 0, 0b00001111), - AmpConfig("DAI2: DC blocking", 0b1, 0x20, 0, 0b00000001), - AmpConfig("DAI2: High sample rate", 0b0, 0x20, 3, 0b00001000), - AmpConfig("ALC enable", 0b1, 0x43, 7, 0b10000000), - AmpConfig("ALC/excursion limiter release time", 0b101, 0x43, 4, 0b01110000), - AmpConfig("ALC multiband enable", 0b1, 0x43, 3, 0b00001000), - AmpConfig("DAI1 EQ enable", 0b0, 0x49, 0, 0b00000001), - AmpConfig("DAI2 EQ clip detection disabled", 0b1, 0x32, 4, 0b00010000), - AmpConfig("DAI2 EQ attenuation", 0x5, 0x32, 0, 0b00001111), - AmpConfig("Excursion limiter upper corner freq", 0b100, 0x41, 4, 0b01110000), - AmpConfig("Excursion limiter lower corner freq", 0b00, 0x41, 0, 0b00000011), - AmpConfig("Excursion limiter threshold", 0b000, 0x42, 0, 0b00001111), - AmpConfig("Distortion limit (THDCLP)", 0x6, 0x46, 4, 0b11110000), - AmpConfig("Distortion limiter release time constant", 0b0, 0x46, 0, 0b00000001), - AmpConfig("Right DAC input mixer: DAI1 left", 0b0, 0x22, 3, 0b00001000), - AmpConfig("Right DAC input mixer: DAI1 right", 0b0, 0x22, 2, 0b00000100), - AmpConfig("Right DAC input mixer: DAI2 left", 0b1, 0x22, 1, 0b00000010), - AmpConfig("Right DAC input mixer: DAI2 right", 0b0, 0x22, 0, 0b00000001), - AmpConfig("DAI1 audio port selector", 0b10, 0x16, 6, 0b11000000), - AmpConfig("DAI1 in/output enable", 0b11, 0x16, 0, 0b11), - AmpConfig("DAI2 audio port selector", 0b01, 0x1E, 6, 0b11000000), - AmpConfig("DAI2 in/output enable", 0b11, 0x1E, 0, 0b11), - AmpConfig("Enable left digital microphone", 0b1, 0x48, 5, 0b00100000), - AmpConfig("Enable right digital microphone", 0b1, 0x48, 4, 0b00010000), - AmpConfig("Enhanced volume smoothing disabled", 0b0, 0x49, 7, 0b10000000), - AmpConfig("Volume adjustment smoothing disabled", 0b0, 0x49, 6, 0b01000000), - AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000), -] - -CONFIGS = { - "tici": [ - AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111), - AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100), - AmpConfig("Right speaker output volume", 0x1c, 0x3E, 0, 0b00011111), - AmpConfig("DAI2 EQ enable", 0b1, 0x49, 1, 0b00000010), - - *configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)), - *configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)), - *configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)), - *configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)), - *configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)), - ], - "tizi": [ - AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111), - AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111), - AmpConfig("Left Speaker Mixer Gain", 0b00, 0x2D, 0, 0b00000011), - AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100), - AmpConfig("Left speaker output volume", 0x17, 0x3D, 0, 0b00011111), - AmpConfig("Right speaker output volume", 0x17, 0x3E, 0, 0b00011111), - - AmpConfig("DAI2 EQ enable", 0b0, 0x49, 1, 0b00000010), - AmpConfig("DAI2: DC blocking", 0b0, 0x20, 0, 0b00000001), - AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000), - AmpConfig("DAI2 EQ attenuation", 0x2, 0x32, 0, 0b00001111), - AmpConfig("Excursion limiter upper corner freq", 0b001, 0x41, 4, 0b01110000), - AmpConfig("Excursion limiter threshold", 0b100, 0x42, 0, 0b00001111), - AmpConfig("Distortion limit (THDCLP)", 0x0, 0x46, 4, 0b11110000), - AmpConfig("Distortion limiter release time constant", 0b1, 0x46, 0, 0b00000001), - AmpConfig("Left DAC input mixer: DAI1 left", 0b0, 0x22, 7, 0b10000000), - AmpConfig("Left DAC input mixer: DAI1 right", 0b0, 0x22, 6, 0b01000000), - AmpConfig("Left DAC input mixer: DAI2 left", 0b1, 0x22, 5, 0b00100000), - AmpConfig("Left DAC input mixer: DAI2 right", 0b0, 0x22, 4, 0b00010000), - AmpConfig("Right DAC input mixer: DAI2 left", 0b0, 0x22, 1, 0b00000010), - AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001), - AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000), - ], -} - -class Amplifier: - AMP_I2C_BUS = 0 - AMP_ADDRESS = 0x10 - - def __init__(self, debug=False): - self.debug = debug - - def _get_shutdown_config(self, amp_disabled: bool) -> AmpConfig: - return AmpConfig("Global shutdown", 0b0 if amp_disabled else 0b1, 0x51, 7, 0b10000000) - - def _set_configs(self, configs: List[AmpConfig]) -> None: - with SMBus(self.AMP_I2C_BUS) as bus: - for config in configs: - if self.debug: - print(f"Setting \"{config.name}\" to {config.value}:") - - old_value = bus.read_byte_data(self.AMP_ADDRESS, config.register, force=True) - new_value = (old_value & (~config.mask)) | ((config.value << config.offset) & config.mask) - bus.write_byte_data(self.AMP_ADDRESS, config.register, new_value, force=True) - - if self.debug: - print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}") - - def set_configs(self, configs: List[AmpConfig]) -> bool: - # retry in case panda is using the amp - for _ in range(10): - try: - self._set_configs(configs) - return True - except OSError: - print("Failed to set amp config, retrying...") - time.sleep(0.02) - return False - - def set_global_shutdown(self, amp_disabled: bool) -> bool: - return self.set_configs([self._get_shutdown_config(amp_disabled), ]) - - def initialize_configuration(self, model: str) -> bool: - cfgs = [ - self._get_shutdown_config(True), - *BASE_CONFIG, - *CONFIGS[model], - self._get_shutdown_config(False), - ] - return self.set_configs(cfgs) - - -if __name__ == "__main__": - with open("/sys/firmware/devicetree/base/model") as f: - model = f.read().strip('\x00') - model = model.split('comma ')[-1] - - if model in CONFIGS: - amp = Amplifier() - amp.initialize_configuration(model) - else: - print(f"Skipping, no config for {model}") diff --git a/userspace/root/usr/comma/sound/sound_init.sh b/userspace/root/usr/comma/sound/sound_init.sh index 6f0c2139..e54f4ad5 100755 --- a/userspace/root/usr/comma/sound/sound_init.sh +++ b/userspace/root/usr/comma/sound/sound_init.sh @@ -1,29 +1,30 @@ #!/bin/bash -/usr/comma/sound/adsp-start.sh - +# The ADSP is auto-booted by the q6v5-pas remoteproc; wait for the ASoC card +# to register. Only mici has a sound card wired up on mainline, so bail out +# cleanly if none appears (e.g. tizi) instead of spinning forever. echo "waiting for sound card to come online" -while [ ! -d /proc/asound/sdm845tavilsndc ] || [ "$(cat /proc/asound/card0/state 2> /dev/null)" != "ONLINE" ] ; do +for _ in $(seq 1 1000); do + [ -d /proc/asound/commamici ] && break sleep 0.01 done +if [ ! -d /proc/asound/commamici ]; then + echo "no sound card, skipping" + exit 0 +fi echo "sound card online" -# Fix permissions for audio group +# Fix permissions for audio group (no udev rule fires for /dev/snd) chgrp audio /dev/snd/* chmod 660 /dev/snd/* -while ! /usr/comma/sound/tinymix controls | grep -q "SEC_MI2S_RX Audio Mixer MultiMedia1"; do +for _ in $(seq 1 1000); do + /usr/comma/sound/tinymix controls | grep -q "SEC_MI2S_RX Audio Mixer MultiMedia1" && break sleep 0.01 done echo "tinymix controls ready" +# MultiMedia1 = playback (hw:0,0), MultiMedia2 = capture (hw:0,1); q6routing +# keys one port per frontend, so playback and capture need distinct frontends. /usr/comma/sound/tinymix set "SEC_MI2S_RX Audio Mixer MultiMedia1" 1 -if grep -q mici /sys/firmware/devicetree/base/model; then - /usr/comma/sound/tinymix set "MultiMedia1 Mixer SEC_MI2S_TX" 1 -else - /usr/comma/sound/tinymix set "MultiMedia1 Mixer TERT_MI2S_TX" 1 - /usr/comma/sound/tinymix set "TERT_MI2S_TX Channels" Two -fi - -# setup the amplifier registers -/usr/local/venv/bin/python /usr/comma/sound/amplifier.py +/usr/comma/sound/tinymix set "MultiMedia2 Mixer SEC_MI2S_TX" 1 diff --git a/userspace/root/usr/local/qr-linux/adsp-start.sh b/userspace/root/usr/local/qr-linux/adsp-start.sh deleted file mode 100755 index 2531fd8c..00000000 --- a/userspace/root/usr/local/qr-linux/adsp-start.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh -############################################################################### -# -# This script is used for administration of the Hexagon DSP -# -# Copyright (c) 2012-2016 Qualcomm Technologies, Inc. -# All Rights Reserved. -# Confidential and Proprietary - Qualcomm Technologies, Inc. -# -############################################################################### - -KEEP_ALIVE=0 -subsys_name="" - -# Wait for adsp.mdt to show up -count=0 -while [ ! -s /firmware/image/adsp.mdt ]; do - sleep 0.1 - # wait 10s for /firmare mounted - count=$(( $count + 1 )) - if [ $count -ge 100 ]; then - echo "[ERROR] Can not find the adsp's firmware" - exit 1 - fi -done - -for subsys in `ls /sys/bus/msm_subsys/devices`; do - name=`cat /sys/bus/msm_subsys/devices/${subsys}/name` - if [ "`cat /sys/bus/msm_subsys/devices/${subsys}/name`" = "adsp" ]; then - subsys_name="${subsys}" - break - fi -done - -if [ "$KEEP_ALIVE" = "1" ]; then - if [ -n "${subsys_name}" ]; then - sysctl -w kernel.panic=0 - echo 1 > /sys/bus/msm_subsys/devices/${subsys_name}/keep_alive - else - echo "[ERROR] Can not keep adsp alive" - fi -fi - -# FIXME: See ATL-3054 -echo 1 > /sys/module/subsystem_restart/parameters/enable_debug -# Bring adsp out of reset -echo "[INFO] Bringing adsp out of reset" -echo 1 > /sys/kernel/boot_adsp/boot - -# wait boot finished -if [ -n "${subsys_name}" ]; then - count=0 - state=`cat /sys/bus/msm_subsys/devices/${subsys_name}/state` - while [ "${state}" != "ONLINE" ]; do - # wait 2s for subsys boot finished - count=$(( $count + 1 )) - if [ $count -ge 200 ]; then - echo "[ERROR] adsp fail to boot" - exit 1 - fi - sleep 0.01 - state=`cat /sys/bus/msm_subsys/devices/${subsys_name}/state` - done -fi - -# Emit adsp -#initctl emit adsp diff --git a/userspace/uv/pyproject.toml b/userspace/uv/pyproject.toml index aa944294..0e0c9f64 100644 --- a/userspace/uv/pyproject.toml +++ b/userspace/uv/pyproject.toml @@ -10,7 +10,7 @@ dependencies = [ # hardware.py "dbus-python", - # amplifier.py + # power_drop_monitor.py "smbus2", # provisioning From 51e7bc0e728d51a227066cd63a095d0280cbee0a Mon Sep 17 00:00:00 2001 From: Trey Moen Date: Sun, 5 Jul 2026 10:53:38 -0700 Subject: [PATCH 2/3] sound: start the adsp remoteproc from sound_init on the mainline kernel The q6v5-pas driver auto-boots the ADSP at probe (~0.5s), but with no initramfs the rootfs firmware isn't available yet, so the load fails with -ENOENT and the remoteproc stays offline permanently -- the sound card never registers (msm-snd-sdm845 deferred-probes forever on "error getting cpu dai name"). Start the adsp explicitly from sound_init, which runs as root after /lib/firmware is mounted, so the baked-in adsp.mbn loads and the q6 audio services come up. Verified on device: card commamici registers, pcm0p/pcm1c present, aplay hw:0,0 and arecord hw:0,1 both run clean with no q6asm errors. --- userspace/root/usr/comma/sound/sound_init.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/userspace/root/usr/comma/sound/sound_init.sh b/userspace/root/usr/comma/sound/sound_init.sh index e54f4ad5..b2c46e4b 100755 --- a/userspace/root/usr/comma/sound/sound_init.sh +++ b/userspace/root/usr/comma/sound/sound_init.sh @@ -1,8 +1,22 @@ #!/bin/bash -# The ADSP is auto-booted by the q6v5-pas remoteproc; wait for the ASoC card -# to register. Only mici has a sound card wired up on mainline, so bail out -# cleanly if none appears (e.g. tizi) instead of spinning forever. +# The q6v5-pas driver tries to auto-boot the ADSP at probe (~0.5s), before the +# rootfs firmware is available, so that load fails and the remoteproc stays +# offline. Start it here, once /lib/firmware is up. Only mici has a sound card +# wired up on mainline, so bail out cleanly if no adsp remoteproc is present. +adsp="" +for rproc in /sys/class/remoteproc/remoteproc*; do + [ "$(cat "$rproc/name" 2>/dev/null)" = adsp ] && adsp="$rproc" && break +done +if [ -z "$adsp" ]; then + echo "no adsp remoteproc, skipping" + exit 0 +fi +if [ "$(cat "$adsp/state")" != running ]; then + echo "starting adsp remoteproc" + echo start > "$adsp/state" +fi + echo "waiting for sound card to come online" for _ in $(seq 1 1000); do [ -d /proc/asound/commamici ] && break From d549c8774e4509dd88082a456651bb693c129ffd Mon Sep 17 00:00:00 2001 From: Trey Moen Date: Wed, 8 Jul 2026 23:11:22 -0700 Subject: [PATCH 3/3] wip, not provenworking --- kernel/dts/sdm845-comma-common.dtsi | 82 +++++++++++++++ kernel/dts/sdm845-comma-mici.dts | 77 +-------------- kernel/dts/sdm845-comma-tizi.dts | 36 +++++++ ...m845-tizi-tertiary-MI2S-and-amp-mclk.patch | 99 +++++++++++++++++++ userspace/root/etc/asound.conf | 6 ++ userspace/root/usr/comma/sound/sound_init.sh | 22 +++-- 6 files changed, 240 insertions(+), 82 deletions(-) create mode 100644 kernel/patches/0018-ASoC-sdm845-tizi-tertiary-MI2S-and-amp-mclk.patch diff --git a/kernel/dts/sdm845-comma-common.dtsi b/kernel/dts/sdm845-comma-common.dtsi index 7a65fb3a..ea129653 100644 --- a/kernel/dts/sdm845-comma-common.dtsi +++ b/kernel/dts/sdm845-comma-common.dtsi @@ -2,6 +2,8 @@ #include #include +#include +#include #include "sdm845.dtsi" #include "pm8998.dtsi" #include "pmi8998.dtsi" @@ -47,6 +49,16 @@ vin-supply = <&vph_pwr>; }; + + spkr_codec: audio-codec-0 { + compatible = "linux,spdif-dit"; + #sound-dai-cells = <0>; + }; + + mic_codec: audio-codec-1 { + compatible = "linux,spdif-dir"; + #sound-dai-cells = <0>; + }; }; &apps_rsc { @@ -520,3 +532,73 @@ status = "okay"; }; + +&adsp_pas { + firmware-name = "qcom/sdm845/adsp.mbn"; + + status = "okay"; +}; + +&q6afedai { + dai@18 { + reg = ; + qcom,sd-lines = <1>; + }; +}; + +&q6asmdai { + dai@0 { + reg = ; + direction = ; + }; + + dai@1 { + reg = ; + direction = ; + }; +}; + +&sound { + compatible = "qcom,sdm845-sndcard"; + pinctrl-names = "default"; + clocks = <&rpmhcc RPMH_LN_BB_CLK2>; + clock-names = "mclk"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + mm2-dai-link { + link-name = "MultiMedia2"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; + }; + }; + + speaker-dai-link { + link-name = "Secondary MI2S Playback"; + cpu { + sound-dai = <&q6afedai SECONDARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&spkr_codec>; + }; + }; +}; + +&tlmm { + sec_mi2s_active: sec-mi2s-active-state { + pins = "gpio80", "gpio81", "gpio82", "gpio83"; + function = "sec_mi2s"; + drive-strength = <8>; + bias-disable; + }; +}; diff --git a/kernel/dts/sdm845-comma-mici.dts b/kernel/dts/sdm845-comma-mici.dts index cbcebe54..b58e9c81 100644 --- a/kernel/dts/sdm845-comma-mici.dts +++ b/kernel/dts/sdm845-comma-mici.dts @@ -1,7 +1,5 @@ /dts-v1/; -#include -#include #include "sdm845-comma-common.dtsi" / { @@ -14,22 +12,6 @@ aliases { hsuart0 = &uart3; }; - - spkr_codec: audio-codec-0 { - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0>; - }; - - mic_codec: audio-codec-1 { - compatible = "linux,spdif-dir"; - #sound-dai-cells = <0>; - }; -}; - -&adsp_pas { - firmware-name = "qcom/sdm845/adsp.mbn"; - - status = "okay"; }; &i2c5 { @@ -75,63 +57,15 @@ }; &q6afedai { - dai@18 { - reg = ; - qcom,sd-lines = <1>; - }; - dai@19 { reg = ; qcom,sd-lines = <0>; }; }; -&q6asmdai { - dai@0 { - reg = ; - direction = ; - }; - - dai@1 { - reg = ; - direction = ; - }; -}; - &sound { - compatible = "qcom,sdm845-sndcard"; - pinctrl-0 = <&sec_mi2s_active>; - pinctrl-names = "default"; model = "comma-mici"; - - mm1-dai-link { - link-name = "MultiMedia1"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; - }; - }; - - mm2-dai-link { - link-name = "MultiMedia2"; - cpu { - sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; - }; - }; - - speaker-dai-link { - link-name = "Secondary MI2S Playback"; - cpu { - sound-dai = <&q6afedai SECONDARY_MI2S_RX>; - }; - - platform { - sound-dai = <&q6routing>; - }; - - codec { - sound-dai = <&spkr_codec>; - }; - }; + pinctrl-0 = <&sec_mi2s_active>; mic-dai-link { link-name = "Secondary MI2S Capture"; @@ -149,15 +83,6 @@ }; }; -&tlmm { - sec_mi2s_active: sec-mi2s-active-state { - pins = "gpio80", "gpio81", "gpio82", "gpio83"; - function = "sec_mi2s"; - drive-strength = <8>; - bias-disable; - }; -}; - &uart3 { status = "okay"; }; diff --git a/kernel/dts/sdm845-comma-tizi.dts b/kernel/dts/sdm845-comma-tizi.dts index d4f8c508..8b708e10 100644 --- a/kernel/dts/sdm845-comma-tizi.dts +++ b/kernel/dts/sdm845-comma-tizi.dts @@ -64,6 +64,42 @@ data-lanes = <0 1 2 3>; }; +&q6afedai { + dai@21 { + reg = ; + qcom,sd-lines = <0 1>; + }; +}; + +&sound { + model = "comma-tizi"; + pinctrl-0 = <&sec_mi2s_active &ter_mi2s_active>; + + mic-dai-link { + link-name = "Tertiary MI2S Capture"; + cpu { + sound-dai = <&q6afedai TERTIARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&mic_codec>; + }; + }; +}; + +&tlmm { + ter_mi2s_active: ter-mi2s-active-state { + pins = "gpio75", "gpio76", "gpio77", "gpio78"; + function = "ter_mi2s"; + drive-strength = <8>; + bias-disable; + }; +}; + &wifi { qcom,calibration-variant = "comma-tizi"; }; diff --git a/kernel/patches/0018-ASoC-sdm845-tizi-tertiary-MI2S-and-amp-mclk.patch b/kernel/patches/0018-ASoC-sdm845-tizi-tertiary-MI2S-and-amp-mclk.patch new file mode 100644 index 00000000..53a19739 --- /dev/null +++ b/kernel/patches/0018-ASoC-sdm845-tizi-tertiary-MI2S-and-amp-mclk.patch @@ -0,0 +1,99 @@ +Subject: [PATCH] ASoC: sdm845: support the comma tizi audio path + +Three additions for the comma tizi (comma 3X) speaker/mic on mainline: + +1. Tertiary MI2S clocking: the tizi mic is on Tertiary MI2S TX. The + machine driver only clocked pri/sec/quat MI2S, so tertiary hit the + default case ("invalid dai id 0x15") and its bit clock was never + enabled -> capture failed with -EIO. Add tertiary cases mirroring + secondary, enabling TER_MI2S_IBIT. + +2. Amp reference clock: the MAX98089 speaker amp derives its DAI PLLs + and DMIC clock from a 19.2 MHz MCLK. Grab an optional "mclk" from + the sound card node (LN_BB_CLK2) and keep it enabled. + +3. LPASS OSR clock (12.288 MHz) out on the sec MI2S MCLK pad (gpio79) + while secondary MI2S is in use; the downstream dts routes it to the + amp's MCLK pin. Harmless on boards that don't mux gpio79. + +diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c +--- a/sound/soc/qcom/sdm845.c ++++ b/sound/soc/qcom/sdm845.c +@@ -4,6 +4,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -39,6 +40,7 @@ + struct snd_soc_card *card; + uint32_t pri_mi2s_clk_count; + uint32_t sec_mi2s_clk_count; ++ uint32_t tert_mi2s_clk_count; + uint32_t quat_tdm_clk_count; + struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; + }; +@@ -220,6 +222,8 @@ + break; + case QUATERNARY_MI2S_RX: + case SECONDARY_MI2S_RX: ++ case TERTIARY_MI2S_RX: ++ case TERTIARY_MI2S_TX: + break; + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); +@@ -368,6 +372,17 @@ + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); ++ } ++ snd_soc_dai_set_fmt(cpu_dai, fmt); ++ snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); ++ break; ++ case TERTIARY_MI2S_RX: ++ case TERTIARY_MI2S_TX: ++ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; ++ if (++(data->tert_mi2s_clk_count) == 1) { ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, ++ MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); + } + snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); +@@ -451,6 +466,15 @@ + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + 0, SNDRV_PCM_STREAM_CAPTURE); ++ } ++ break; ++ ++ case TERTIARY_MI2S_RX: ++ case TERTIARY_MI2S_TX: ++ if (--(data->tert_mi2s_clk_count) == 0) { ++ snd_soc_dai_set_sysclk(cpu_dai, ++ Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, ++ 0, SNDRV_PCM_STREAM_CAPTURE); + } + break; + +@@ -586,6 +619,7 @@ + struct snd_soc_card *card; + struct sdm845_snd_data *data; + struct device *dev = &pdev->dev; ++ struct clk *mclk; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); +@@ -597,6 +631,10 @@ + if (!data) + return -ENOMEM; + ++ mclk = devm_clk_get_optional_enabled(dev, "mclk"); ++ if (IS_ERR(mclk)) ++ return dev_err_probe(dev, PTR_ERR(mclk), "failed to enable mclk\n"); ++ + card->driver_name = DRIVER_NAME; + card->dapm_widgets = sdm845_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); diff --git a/userspace/root/etc/asound.conf b/userspace/root/etc/asound.conf index 700bb3cf..2f881b5c 100644 --- a/userspace/root/etc/asound.conf +++ b/userspace/root/etc/asound.conf @@ -27,3 +27,9 @@ pcm.comma_default_commamici { playback.pcm "plughw:0,0" capture.pcm "plughw:0,1" } + +pcm.comma_default_commatizi { + type asym + playback.pcm "plughw:0,0" + capture.pcm "plughw:0,1" +} diff --git a/userspace/root/usr/comma/sound/sound_init.sh b/userspace/root/usr/comma/sound/sound_init.sh index b2c46e4b..0a8ff04e 100755 --- a/userspace/root/usr/comma/sound/sound_init.sh +++ b/userspace/root/usr/comma/sound/sound_init.sh @@ -2,8 +2,8 @@ # The q6v5-pas driver tries to auto-boot the ADSP at probe (~0.5s), before the # rootfs firmware is available, so that load fails and the remoteproc stays -# offline. Start it here, once /lib/firmware is up. Only mici has a sound card -# wired up on mainline, so bail out cleanly if no adsp remoteproc is present. +# offline. Start it here, once /lib/firmware is up. Bail out cleanly if no adsp +# remoteproc is present. adsp="" for rproc in /sys/class/remoteproc/remoteproc*; do [ "$(cat "$rproc/name" 2>/dev/null)" = adsp ] && adsp="$rproc" && break @@ -18,15 +18,19 @@ if [ "$(cat "$adsp/state")" != running ]; then fi echo "waiting for sound card to come online" +card="" for _ in $(seq 1 1000); do - [ -d /proc/asound/commamici ] && break + for c in /proc/asound/comma*; do + [ -d "$c" ] && card="$(basename "$c")" + done + [ -n "$card" ] && break sleep 0.01 done -if [ ! -d /proc/asound/commamici ]; then +if [ -z "$card" ]; then echo "no sound card, skipping" exit 0 fi -echo "sound card online" +echo "sound card online: $card" # Fix permissions for audio group (no udev rule fires for /dev/snd) chgrp audio /dev/snd/* @@ -40,5 +44,11 @@ echo "tinymix controls ready" # MultiMedia1 = playback (hw:0,0), MultiMedia2 = capture (hw:0,1); q6routing # keys one port per frontend, so playback and capture need distinct frontends. +# tizi captures off Tertiary MI2S TX (2ch); mici off Secondary MI2S TX. /usr/comma/sound/tinymix set "SEC_MI2S_RX Audio Mixer MultiMedia1" 1 -/usr/comma/sound/tinymix set "MultiMedia2 Mixer SEC_MI2S_TX" 1 +if [ "$card" = commamici ]; then + /usr/comma/sound/tinymix set "MultiMedia2 Mixer SEC_MI2S_TX" 1 +else + /usr/comma/sound/tinymix set "MultiMedia2 Mixer TERT_MI2S_TX" 1 + /usr/comma/sound/tinymix set "TERT_MI2S_TX Channels" Two +fi