commit 9aa96cb51d19c22ff25ecaa75488ca870e9c44b3 Author: Pat Nakajima Date: Wed May 13 20:23:09 2026 +0000 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0dae89c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +cache/ +build/ +mnt/ \ No newline at end of file diff --git a/audio/asound.conf b/audio/asound.conf new file mode 100644 index 0000000..1cf5837 --- /dev/null +++ b/audio/asound.conf @@ -0,0 +1,60 @@ +defaults.pcm.rate_converter "samplerate_best" + +pcm.!default { + type plug + slave { + pcm "filtered" + rate 48000 + } +} + +pcm.filtered { + type ladspa + slave.pcm "out" + path "/usr/lib/ladspa" + plugins [ + { + label highpass_iir + input { + controls [ 100 1 ] + } + } + { + label hardLimiter + input { + controls [ 0 1 0 ] + } + } + ] +} + +pcm.out { + type plug + slave { + pcm "hw:CARD=toesaudio,DEV=1" + format S16_LE + } +} + +pcm.toes_playback { + type plug + slave { + pcm "out" + rate 48000 + } +} + +pcm.toes_capture { + type plug + slave { + pcm "hw:CARD=toesaudio,DEV=0" + format S32_LE + rate 48000 + channels 2 + } +} + +ctl.!default { + type hw + card toesaudio +} \ No newline at end of file diff --git a/audio/build-audio.sh b/audio/build-audio.sh new file mode 100755 index 0000000..e17b35a --- /dev/null +++ b/audio/build-audio.sh @@ -0,0 +1,13 @@ +#! /bin/bash +set -ex + +# builds the audio overlay _inside_ the image. + +modprobe i2c-dev || true +modprobe snd-soc-simple-card || true +modprobe snd-soc-tlv320aic31xx || true +modprobe snd-soc-ics43432 || true + +dtc -@ -I dts -O dtb -o \ + "/boot/firmware/overlays/toesaudio.dtbo" \ + "/tmp/audio/toesaudio.dts" diff --git a/audio/init-audio b/audio/init-audio new file mode 100644 index 0000000..6fd2fe8 --- /dev/null +++ b/audio/init-audio @@ -0,0 +1,83 @@ +#!/bin/bash +set -euo pipefail + +CARD_ID="toesaudio" +CARD_NAME="toesaudio" +I2C_BUS="${I2C_BUS:-1}" +CODEC_ADDR="${CODEC_ADDR:-0x18}" +MIC_THSEL_LEVEL="${MIC_THSEL_LEVEL:-high}" +DAC=${1:-127} +DRIVER=${2:-0} +ANALOG=${3:-50} + +find_card() { + awk -v id="$CARD_ID" -v name="$CARD_NAME" ' + index($0, "[" id "]") || index($0, name) { print $1; exit } + ' /proc/asound/cards +} + +codec_ready() { + i2cget -y -f "$I2C_BUS" "$CODEC_ADDR" 0 >/dev/null 2>&1 +} + +set_mixer() { + amixer -q -c "$CARD" "$@" >/dev/null 2>&1 || true +} + +set_mic_pins() { + if ! command -v pinctrl >/dev/null 2>&1; then + return 0 + fi + + case "$MIC_THSEL_LEVEL" in + low) + pinctrl set 8 op pn dl >/dev/null 2>&1 || true + ;; + *) + pinctrl set 8 op pn dh >/dev/null 2>&1 || true + ;; + esac + + pinctrl set 13 ip pn >/dev/null 2>&1 || true +} + +CARD="" +for _ in $(seq 1 20); do + CARD="$(find_card || true)" + if [ -n "$CARD" ]; then + break + fi + sleep 1 +done + +if [ -z "$CARD" ]; then + echo "toesaudio card not present; skipping restore." + exit 0 +fi + +for _ in $(seq 1 20); do + if codec_ready; then + break + fi + sleep 1 +done + +if ! codec_ready; then + echo "TLV320 codec not responding on i2c-${I2C_BUS} addr ${CODEC_ADDR}; skipping restore." + exit 0 +fi + +set_mic_pins +set_mixer sset 'DAC Left Input' 'Left Data' +set_mixer sset 'DAC Right Input' 'Right Data' +set_mixer sset 'Output Left From Left DAC' on +set_mixer sset 'Output Right From Right DAC' on +set_mixer sset 'Speaker' on +set_mixer sset 'Speaker Driver' "$DRIVER" +set_mixer sset 'Speaker Driver' on +set_mixer sset 'Speaker Analog' "$ANALOG" +set_mixer sset 'DAC' "${DAC},${DAC}" +set_mixer sset 'HP Left' off +set_mixer sset 'HP Right' off +set_mixer sset 'HP Driver' '0,0' +set_mixer sset 'HP Driver' off diff --git a/audio/init-audio.service b/audio/init-audio.service new file mode 100644 index 0000000..64b5611 --- /dev/null +++ b/audio/init-audio.service @@ -0,0 +1,11 @@ +[Unit] +Description=Restore barepi TLV320 speaker routing +After=sound.target +Wants=sound.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/init-audio 127 0 50 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/audio/toesaudio.dts b/audio/toesaudio.dts new file mode 100644 index 0000000..5a18404 --- /dev/null +++ b/audio/toesaudio.dts @@ -0,0 +1,137 @@ +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2712"; + + fragment@0 { + target = <&i2s_clk_producer>; + __overlay__ { + status = "okay"; + }; + }; + + fragment@1 { + target-path = "/"; + __overlay__ { + tlv320_dvdd: regulator-tlv320-dvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-dvdd-1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + tlv320_iovdd: regulator-tlv320-iovdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-iovdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_avdd: regulator-tlv320-avdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-avdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_hpvdd: regulator-tlv320-hpvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-hpvdd-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + tlv320_spkvdd: regulator-tlv320-spkvdd { + compatible = "regulator-fixed"; + regulator-name = "tlv320-spkvdd-5v0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + /* + * The T5848 is a control-less I2S microphone from Linux's point + * of view, so use the generic ICS43432 codec DAI as a simple + * capture endpoint and keep the speaker path unchanged. + */ + mic_codec: t5848-codec { + compatible = "invensense,ics43432"; + #sound-dai-cells = <0>; + }; + }; + }; + + fragment@2 { + target = <&i2c_arm>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + dac_codec: tlv320dac3100@18 { + compatible = "ti,tlv320dac3100"; + reg = <0x18>; + #sound-dai-cells = <0>; + AVDD-supply = <&tlv320_avdd>; + IOVDD-supply = <&tlv320_iovdd>; + DVDD-supply = <&tlv320_dvdd>; + HPVDD-supply = <&tlv320_hpvdd>; + SPLVDD-supply = <&tlv320_spkvdd>; + SPRVDD-supply = <&tlv320_spkvdd>; + reset-gpios = <&gpio 12 1>; /* GPIO_ACTIVE_LOW */ + ti,pll-clkin-src = <1>; + status = "okay"; + }; + }; + }; + + fragment@3 { + target-path = "/"; + __overlay__ { + sound { + compatible = "simple-audio-card"; + #address-cells = <1>; + #size-cells = <0>; + simple-audio-card,name = "toesaudio"; + simple-audio-card,format = "i2s"; + status = "okay"; + + simple-audio-card,dai-link@0 { + reg = <0>; + format = "i2s"; + bitclock-master = <&cpu0>; + frame-master = <&cpu0>; + + cpu0: cpu { + sound-dai = <&i2s_clk_producer>; + }; + + codec0: codec { + sound-dai = <&dac_codec>; + system-clock-frequency = <1536000>; + }; + }; + + simple-audio-card,dai-link@1 { + reg = <1>; + format = "i2s"; + bitclock-master = <&cpu1>; + frame-master = <&cpu1>; + + cpu1: cpu { + sound-dai = <&i2s_clk_producer>; + }; + + codec1: codec { + sound-dai = <&mic_codec>; + }; + }; + }; + }; + }; +}; \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..8b7cf1d --- /dev/null +++ b/build.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +set -ex + +scripts/check-qemu.sh +scripts/download-base.sh +scripts/setup-mount.sh +scripts/copy-files.sh +scripts/update-configtxt.sh +scripts/build-in-chroot.sh diff --git a/scripts/bootstrap-repo.sh b/scripts/bootstrap-repo.sh new file mode 100755 index 0000000..4103d17 --- /dev/null +++ b/scripts/bootstrap-repo.sh @@ -0,0 +1,21 @@ +#! /bin/bash +set -ex + +# bootstrap _this_ repo (toes-image-builder), not the image itself. +# should only have to do this once per host we run this on. + +sudo apt-get update +sudo apt-get install -y \ + qemu-user-static \ + binfmt-support \ + qemu-system-arm \ + qemu-utils \ + kpartx \ + parted \ + fdisk \ + dosfstools \ + e2fsprogs \ + xz-utils \ + rsync \ + curl \ + ca-certificates diff --git a/scripts/build-in-chroot.sh b/scripts/build-in-chroot.sh new file mode 100755 index 0000000..fdbac72 --- /dev/null +++ b/scripts/build-in-chroot.sh @@ -0,0 +1,49 @@ +#! /bin/bash +set -ex + +# runs _inside_ the image, under qemu arm64. + +echo "====> mounting" +sudo chroot mnt/root /bin/bash + +echo "====> updating apt" +apt-get update +echo "====> installing apt packages" +apt-get install -y \ + ca-certificates \ + curl \ + jq \ + git \ + locales \ + network-manager \ + pkg-config \ + libdbus-1-dev \ + avahi-daemon \ + avahi-utils \ + libavahi-client-dev \ + bluez \ + build-essential \ + python3-dev \ + python3-pip \ + sox \ + alsa-utils \ + libasound2-plugins \ + swh-plugins \ + i2c-tools \ + device-tree-compiler \ + unzip \ + "linux-modules-$(uname -r)" + +# setup locale +locale-gen en_US.UTF-8 +update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 + +echo "====> installing rgbled" +curl -fsSL https://git.nose.space/nakajima/rgbled/releases/download/v0.1.1/install.sh | bash + +echo "====> installing bun" +curl -fsSL https://bun.com/install | bash + +echo "====> installing baudy" +cd /tmp +git clone https://git.nose.space/nakajima/baudy diff --git a/scripts/check-qemu.sh b/scripts/check-qemu.sh new file mode 100755 index 0000000..69898b5 --- /dev/null +++ b/scripts/check-qemu.sh @@ -0,0 +1,21 @@ +#! /bin/bash +set -ex + +# make sure the repo is properly setup by bootstrap-repo.sh + +required_binfmt="/proc/sys/fs/binfmt_misc/qemu-aarch64" + +if [ ! -e "$required_binfmt" ]; then + echo "missing qemu-aarch64 binfmt registration" >&2 + exit 1 +fi + +if ! grep -q '^enabled$' "$required_binfmt"; then + echo "qemu-aarch64 binfmt is not enabled" >&2 + exit 1 +fi + +if ! grep -q 'flags:.*F' "$required_binfmt"; then + echo "qemu-aarch64 binfmt lacks F flag; this builder expects systemd-binfmt with fixed interpreter" >&2 + exit 1 +fi diff --git a/scripts/copy-files.sh b/scripts/copy-files.sh new file mode 100755 index 0000000..11227d2 --- /dev/null +++ b/scripts/copy-files.sh @@ -0,0 +1,13 @@ +#! /bin/bash +set -ex + +# copy files from this repo to _inside_ the image +cp audio/toesaudio.dts mnt/root/tmp/toesaudio.dts +cp audio/asound.conf mnt/root/etc/asound.conf +cp audio/init-audio mnt/root/usr/local/sbin/init-audio +cp audio/init-audio.service mnt/root/etc/systemd/system/init-audio.service +cp audio/build-audio.sh mnt/root/tmp/build-audio.sh + +# make sure these are executable +chmod +x mnt/root/usr/local/sbin/init-audio +chmod +x mnt/root/tmp/build-audio.sh diff --git a/scripts/download-base.sh b/scripts/download-base.sh new file mode 100755 index 0000000..331a7d5 --- /dev/null +++ b/scripts/download-base.sh @@ -0,0 +1,15 @@ +#! /bin/bash +set -ex + +# downloads the ubuntu base image + +if [ -f "cache/ubuntu-24.04.4-preinstalled-server-arm64+raspi.img.xz" ]; then + exit 0 +fi + +mkdir -p cache +cd cache +curl -LO https://cdimage.ubuntu.com/releases/noble/release/ubuntu-24.04.4-preinstalled-server-arm64+raspi.img.xz +curl -LO https://cdimage.ubuntu.com/releases/noble/release/SHA256SUMS +curl -LO https://cdimage.ubuntu.com/releases/noble/release/SHA256SUMS.gpg +sha256sum -c SHA256SUMS --ignore-missing diff --git a/scripts/setup-mount.sh b/scripts/setup-mount.sh new file mode 100755 index 0000000..bc24a94 --- /dev/null +++ b/scripts/setup-mount.sh @@ -0,0 +1,18 @@ +#! /bin/bash +set -ex + +# unpacks the base image so we can do our stuff to it + +mkdir -p build +xz -dkc cache/ubuntu-24.04.4-preinstalled-server-arm64+raspi.img.xz >build/toes-ubuntu-base.img + +loop_device="$(sudo losetup --find --show --partscan build/toes-ubuntu-base.img)" + +echo "$loop_device" + +lsblk "$loop_device" + +mkdir -p mnt/root mnt/boot + +sudo mount "${loop_device}p2" mnt/root +sudo mount "${loop_device}p1" mnt/boot diff --git a/scripts/update-configtxt.sh b/scripts/update-configtxt.sh new file mode 100644 index 0000000..62196c4 --- /dev/null +++ b/scripts/update-configtxt.sh @@ -0,0 +1,22 @@ +#! /bin/bash +set -ex + +# add stuff we want to config.txt +add = <<-END + dtparam=spi=on + # we can adjust these values as needed + dtparam=cooling_fan=on,fan_temp0=62000,fan_temp0_hyst=8000,fan_temp0_speed=80,fan_temp1=70000,fan_temp1_hyst=5000,fan_temp1_speed=100,fan_temp2=76000,fan_temp2_hyst=4000,fan_temp2_speed=170,fan_temp3=80000,fan_temp3_hyst=3000,fan_temp3_speed=250 + # enable i2s for audio + dtparam=i2s=on + # use an external antenna instead of the pcb antenna on the CM + dtparam=ant2 + # load our audio driver + dtoverlay=toesaudio + # enable gpio12 which we use to reset the audio codec + gpio=12=op,dh +END + +sed -i "s/dtparam=spi=on/$add" mnt/boot/config.txt + +# we don't need fancy graphics. we don't have a screen. +echo "\ngpu_mem=16" >>mnt/boot/config.txt