Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gueritta/primemixxx/llms.txt

Use this file to discover all available pages before exploring further.

The primemixxx SD card is a microSD formatted as ext4 with label TKGL_BOOTSTRAP. It contains everything MIXXX needs to run — the binary, Qt 5.15.8 libraries, audio codecs, MIDI mappings, settings, and the launcher script. The device’s internal eMMC is never modified at runtime; removing the SD card and rebooting restores stock Engine OS.

SD Card Identity

PropertyValue
LabelTKGL_BOOTSTRAP
Filesystemext4
Mount point/media/TKGL_BOOTSTRAP (mounted by the TKGL stub at boot)
Activationecho external > /sys/bus/platform/devices/sd-mux/state
MIXXX bundle root/media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/ ($BUNDLE)
Internal eMMC mirror/media/az01-internal/mixxx/ ($INTERNAL_BUNDLE, used when present for faster loading)

Directory Structure

The MIXXX bundle lives at /media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/. Everything MIXXX needs at runtime — libraries, plugins, mappings, and settings — is self-contained here.
/media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/
├── mixxx.real                        # 17 MB binary — DO NOT USE (causes EGLFS crash)
├── bin/
│   └── mixxx -> ../lib/bin/mixxx     # Symlink to working 10 MB binary
├── lib/
│   ├── bin/
│   │   └── mixxx                     # MIXXX 2.5.6 ARMv7 (~10 MB, working binary)
│   ├── libQt5*.so.5                  # Qt 5.15.8 (bundled — device's 5.15.2 not used)
│   ├── libEGL.so -> /usr/lib/libmali.so.14.0   # Mali r1p0 shim
│   ├── libGLESv2.so -> /usr/lib/libmali.so.14.0
│   ├── libasound.so.2                # ALSA
│   ├── libportaudio.so.2             # Audio I/O
│   ├── libFLAC.so.12                 # Codec
│   ├── libmp3lame.so.0               # MP3 codec
│   ├── librubberband.so.2            # Time stretching
│   ├── libkeyfinder.so.2             # Key detection
│   ├── libchromaprint.so.1           # BPM detection
│   └── ...                           # 66+ total shared libraries
├── qt-plugins/
│   ├── platforms/
│   │   └── libqeglfs.so              # EGLFS platform plugin
│   ├── egldeviceintegrations/
│   │   └── libqeglfs-mali-integration.so  # Custom Mali integration (critical)
│   ├── generic/
│   │   └── libqevdevtouchplugin.so   # Touchscreen input
│   ├── imageformats/                 # SVG, ICO support
│   ├── sqldrivers/
│   │   └── libqsqlite.so             # SQLite for MIXXX library DB
│   └── platformthemes/
├── mixxx-mapping/                    # MIDI controller mappings (source reference)
│   ├── Denon-Prime-4*.xml
│   ├── Denon-Prime-4*-scripts.js
│   └── prime-go/
│       ├── Denon-Prime-Go*.xml
│       └── Denon-Prime-Go*-scripts.js
├── mixxx_launcher.sh                 # SD card launcher (env vars + CPU shielding)
├── settings/                         # MIXXX runtime settings
│   ├── mixxx.cfg
│   └── mixxxdb.sqlite                # Library database
├── share/
│   └── mixxx/                        # Resource path (skins, translations)
├── music/                            # USB music library mount point
└── backup/                           # Device binary backup
Two MIXXX binaries exist — only one works. lib/bin/mixxx (~10 MB, stripped) is the working binary. mixxx.real (~17 MB, unstripped) crashes immediately with:
EGLFS: OpenGL windows cannot be mixed with others
The bin/mixxx symlink must point to ../lib/bin/mixxx. Never redirect it to ../mixxx.real. Verify the symlink target with:
ls -la /media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/bin/mixxx
# Should print: bin/mixxx -> ../lib/bin/mixxx

Launcher Script

The launcher at mixxx_launcher.sh is the real entry point for MIXXX. The TKGL bootstrap stub on internal eMMC mounts the SD card and then executes the launcher directly from the TKGL SD card path. The full SD card launcher auto-detects the device model, sets environment variables, mounts the USB music library, applies I/O and VM tuning, and then applies CPU shielding after MIXXX launches:
#!/bin/sh
# MIXXX Launcher — Denon Prime Go / Prime 4 (SD card)
# Auto-detects device model via DRM connector and applies correct display settings.

# Guard against duplicate instances (systemd-run may restart before old process exits)
if pidof mixxx > /dev/null 2>&1; then
    echo "Mixxx already running, exiting."
    exit 0
fi

# Detect device model from DRM connector
# Prime Go  = DSI-1  (rotation 90,  no vsync needed)
# Prime 4   = LVDS-1 (rotation 270, needs SWAPINTERVAL=1)
detect_device() {
    if [ -f /sys/class/drm/card0-DSI-1/status ] && \
       grep -q connected /sys/class/drm/card0-DSI-1/status 2>/dev/null; then
        DEVICE="primego"
        ROTATION=90
        SWAPINTERVAL=0
    else
        DEVICE="prime4"
        ROTATION=270
        SWAPINTERVAL=1
    fi
    echo "[launcher] detected device: $DEVICE (rotation=$ROTATION swapinterval=$SWAPINTERVAL)"
}
detect_device

BUNDLE=/media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle
MUSIC_DIR="$BUNDLE/music"

# Internal eMMC storage for binaries and libraries (50MHz/8-bit bus vs 25MHz/4-bit SD).
# Loading Qt 5.15.8, Mali integration, and MIXXX binary from fast internal storage
# eliminates MMC bus contention on the external SD during audio playback.
INTERNAL_BUNDLE="/media/az01-internal/mixxx"
if [ -d "$INTERNAL_BUNDLE/lib" ] && [ -f "$INTERNAL_BUNDLE/lib/bin/mixxx" ]; then
    BIN_PATH="$INTERNAL_BUNDLE/bin"
    LIB_PATH="$INTERNAL_BUNDLE/lib"
    PLUGIN_PATH="$INTERNAL_BUNDLE/qt-plugins"
else
    echo "[launcher] internal lib not found, falling back to SD bundle"
    BIN_PATH="$BUNDLE/bin"
    LIB_PATH="$BUNDLE/lib"
    PLUGIN_PATH="$BUNDLE/qt-plugins"
fi

# USB Music Library: mount USB drive to music directory if present
mount_usb_music() {
    [ -d "$MUSIC_DIR" ] || mkdir -p "$MUSIC_DIR"
    mountpoint -q "$MUSIC_DIR" && return 0
    for dev in /dev/sda1 /dev/sdb1; do
        if [ -b "$dev" ]; then
            mount -o ro "$dev" "$MUSIC_DIR" 2>/dev/null && return 0
        fi
    done
    return 1
}
mount_usb_music

# Work around kernel 6.1 hidraw netlink bug — udev's hid_enumerate() hangs without NLMSG_DONE
export LD_PRELOAD=$LIB_PATH/no_hid_poll.so
# Qt 5.15.8 plugins from SD card (NOT device's Qt 5.15.2 — eglfs_emu can't take over fbcon)
export QT_PLUGIN_PATH="$PLUGIN_PATH"
# SD Qt 5.15.8 first, device Qt 5.15.2 fallback, then system libs
export LD_LIBRARY_PATH="$LIB_PATH:/usr/qt/lib:/usr/lib"
export QT_QPA_PLATFORM=eglfs
export QT_QPA_EGLFS_INTEGRATION=eglfs_mali
export QT_QPA_EGLFS_SWAPINTERVAL="$SWAPINTERVAL"
export QT_QPA_EGLFS_ROTATION="$ROTATION"
export QT_QPA_FONTDIR=/usr/share/fonts
export QT_QPA_GENERIC_PLUGINS="evdevtouch:/dev/input/event0 evdevkeyboard:/dev/input/event1"
export QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS="/dev/input/event0:rotate=$ROTATION:minX=0:maxX=1280:minY=0:maxY=800"
export QT_QPA_EGLFS_PHYSICAL_WIDTH=155
export QT_QPA_EGLFS_PHYSICAL_HEIGHT=98
export HOME=/tmp
export XDG_RUNTIME_DIR=/tmp

# Disable RT throttling — audio threads need 100% of their timeslice.
echo -1 > /proc/sys/kernel/sched_rt_runtime_us

# I/O & VM latency optimizations
for blk in mmcblk0 mmcblk1; do
    echo none > "/sys/block/$blk/queue/scheduler" 2>/dev/null
    echo 512 > "/sys/block/$blk/queue/read_ahead_kb" 2>/dev/null
    echo 64 > "/sys/block/$blk/queue/nr_requests" 2>/dev/null
done
echo 20480 > /proc/sys/vm/dirty_background_bytes 2>/dev/null
echo 1 > /proc/sys/vm/swappiness
echo 200 > /proc/sys/vm/vfs_cache_pressure
echo 10 > /proc/sys/vm/stat_interval
for mp in /data /media/az01-internal /media/TKGL_BOOTSTRAP; do
    mountpoint -q "$mp" 2>/dev/null && mount -o remount,noatime,nodiratime,commit=60 "$mp" 2>/dev/null
done

# Launch MIXXX pinned to CPU cores 2-3 (audio-dedicated cores).
# We do NOT set RT priority on the main process — that would cause ALL
# 44+ child threads to inherit SCHED_FIFO and compete with audio.
taskset -c 2,3 $BIN_PATH/mixxx -platform eglfs --settingsPath $BUNDLE/settings --resourcePath $BUNDLE "$@" &
MIXPID=$!

# Post-launch: selectively boost audio-critical threads, banish everything else
BANISH_PATTERNS="mali-|CachingReader|QEvdevTouch|StatsManager|QDBus|VinylControl|LibraryScanner|BrowseThread|AnalyzerThread|Controller$|VSync|gmain|gdbus|Thread \(pooled\)|QQuickPixmapRea|QQmlThread|libusb_event"

for i in $(seq 1 12); do
    sleep 1
    for tid in $(ls /proc/$MIXPID/task/ 2>/dev/null); do
        name=$(cat /proc/$MIXPID/task/$tid/comm 2>/dev/null)
        case "$name" in
            EngineWorkerSch|EngineSideChain)
                chrt -f -p 98 $tid 2>/dev/null
                taskset -p 0x0C $tid 2>/dev/null
                ;;
        esac
        if echo "$name" | grep -qE "$BANISH_PATTERNS"; then
            chrt -o -p 0 $tid 2>/dev/null
            taskset -p 0x03 $tid 2>/dev/null
        fi
    done
done
# Pin main process to non-audio cores 0-1 at SCHED_OTHER so newly-spawned
# threads (e.g. Qt thread pool) inherit non-audio affinity by default.
chrt -o -p 0 $MIXPID 2>/dev/null
taskset -p 0x03 $MIXPID 2>/dev/null

wait $MIXPID

Key Environment Variables

VariableValuePurpose
LD_LIBRARY_PATH$LIB_PATH:/usr/qt/lib:/usr/libBundle libs first, then device Qt 5.15.2 fallback, then system
QT_PLUGIN_PATH$PLUGIN_PATHQt 5.15.8 plugins (EGLFS Mali integration)
QT_QPA_PLATFORMeglfsFramebuffer display, no X11/Wayland
QT_QPA_EGLFS_INTEGRATIONeglfs_maliCustom Mali integration plugin (critical for display)
QT_QPA_EGLFS_ROTATION90 (Prime Go) or 270 (Prime 4)Set dynamically by detect_device(); logical screen is 1280×800 landscape
QT_QPA_EGLFS_SWAPINTERVAL0 (Prime Go) or 1 (Prime 4)Prime 4 DSI-1 needs vsync to prevent tearing; Prime Go LVDS-1 does not
QT_QPA_GENERIC_PLUGINSevdevtouch:/dev/input/event0 evdevkeyboard:/dev/input/event1Touchscreen and keyboard input (space-separated)
QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS/dev/input/event0:rotate=$ROTATION:minX=0:maxX=1280:minY=0:maxY=800Maps ILI2117 physical coordinates to Qt’s rotated coordinate system
QT_QPA_EGLFS_PHYSICAL_WIDTH155Physical display width in mm (Prime Go 5″ display)
QT_QPA_EGLFS_PHYSICAL_HEIGHT98Physical display height in mm
LD_PRELOAD$LIB_PATH/no_hid_poll.soSkips hidraw udev scan (kernel 6.1.111 never sends NLMSG_DONE)
HOME/tmpAvoids writing to /root on read-only rootfs
XDG_RUNTIME_DIR/tmpRequired by Qt/D-Bus on embedded systems

Audio Optimization Stack

LayerConfiguration
Audio threadsEngineWorkerSch + EngineSideChain at SCHED_FIFO 98, pinned to cores 2-3
Main processSCHED_OTHER, pinned to cores 0-1 (so newly spawned threads inherit non-audio affinity)
Non-audio threadsSCHED_OTHER, banished to cores 0-1 (Mali GPU, CachingReader, Qt pool, GLib, etc.)
RT throttlingDisabled (sched_rt_runtime_us=-1)
IRQ affinityAll non-audio IRQs (GPU, USB, MMC) pinned to CPU 0 at boot
ALSA devicehw:JP11,0 — direct, no plughw software conversion
ALSA formatS32_LE, 4 channels, 44100 Hz, latency=5 (period 1024 = 23.2ms)
I/O schedulerBFQ → none on both MMC blocks; read-ahead 512 KB; nr_requests 64
VM tuningdirty_background_bytes=20MB, swappiness=1, vfs_cache_pressure=200
FilesystemAll ext4 mounts with noatime,nodiratime,commit=60

USB MP3 Library Setup

MIXXX’s sandbox silently blocks vfat filesystems, which means a USB drive formatted as FAT32 will mount successfully but MIXXX will not scan it. The solution is to bind-mount the USB device to an ext4-backed path that MIXXX trusts. The launcher’s mount_usb_music() function handles this automatically — it mounts /dev/sda1 or /dev/sdb1 (read-only) to $BUNDLE/music/. MIXXX then scans that path as a normal ext4 directory.

Library Directories: SQLite, Not mixxx.cfg

Music library paths are stored in the directories table of mixxxdb.sqlite, not in mixxx.cfg. The [Recording] Directory key in mixxx.cfg controls only the recording save path, not the music library. RescanOnStartup=1 does nothing if the directories table is empty.
To check whether the library directory is registered:
sqlite3 /media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/settings/mixxxdb.sqlite \
  "SELECT directory FROM directories;"
If the query returns nothing, insert the path manually:
sqlite3 /media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/settings/mixxxdb.sqlite \
  "INSERT INTO directories (id, directory, volume) VALUES (1, '/media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/music', 1);"
After inserting, set RescanOnStartup=1 in mixxx.cfg, restart MIXXX once to trigger the scan, then set it back to 0.

mixxx.cfg Reference

[Config]
FirstRun=1
HasScreenedForLibraryDir=1

[Library]
Directory[0]=/media/TKGL_BOOTSTRAP/tkgl_bootstrap_DenonPrimeGO/mixxx-bundle/music
RescanOnStartup=0

System Libraries NOT on the SD Card

These nine libraries are provided by the device’s own /lib and must never appear in mixxx-bundle/lib/. Bundling them causes a kernel ABI mismatch that produces SIGABRT or SIGSEGV at launch.
LibraryRole
libc.so.6C standard library
libm.so.6Math library
libpthread.so.0POSIX threads
libdl.so.2Dynamic linker
librt.so.1POSIX real-time extensions
libstdc++.so.6C++ standard library
libgcc_s.so.1GCC runtime
ld-linux-armhf.so.3Dynamic linker/loader
libatomic.so.1GCC atomic operations
Run scripts/dev-fix-device-libs.sh after dev-collect-mixxx-bundle.sh to remove these automatically.

Deployment Scripts Summary

ScriptPurpose
scripts/dev-collect-mixxx-bundle.shGather MIXXX + deps from Buildroot output into mixxx-bundle/
scripts/dev-fix-device-libs.shRemove system-critical libs from the deployed bundle on device
scripts/dev-deploy-to-device.shSCP bundle to device, install services, back up SD card
scripts/dev-quick-fix-deploy.shRedeploy only changed files for fast iteration
scripts/dev-create-sdcard-bundle.shAssemble complete SD card tarball for distribution

Build docs developers (and LLMs) love