#!/bin/sh
# SPDX-License-Identifier: GPL-3.0+
# Copyright 2021-2024 Lukas F. Hartmann <lukas@mntre.com>
# Copyright 2022-2025 Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>

set -eu

usage() {
  echo "Hardware setup specific to your MNT Reform platform." >&2
  echo "Usually run by /lib/systemd/system/reform-hw-setup.service" >&2
  echo >&2
  echo "Usage: $0 [--help]" >&2
  echo >&2
  echo "Options:" >&2
  echo "  --help           Display this help and exit." >&2
}

if [ "$#" -gt 0 ]; then
  if [ "$1" != "--help" ]; then
    echo "E: too many arguments" >&2
    usage
    exit 1
  fi
  usage
  exit 0
fi

if [ "$(id -u)" -ne 0 ]; then
  echo "reform-hw-setup has to be run as root / using sudo and is usually run by /lib/systemd/system/reform-hw-setup.service" >&2
  exit 1
fi

init_a311d() {
  # A311D sound controls
  # without setting these, there is no UCM profile and messages like this are produced:
  # no backend DAIs enabled for fe.dai-link-0, possibly missing ALSA mixer-based routing or UCM profile
  amixer -c 0 sset 'FRDDR_A SINK 1 SEL' 'OUT 1'
  amixer -c 0 sset 'FRDDR_A SRC 1 EN' 'on'
  amixer -c 0 sset 'TDMOUT_B SRC SEL' 'IN 0'
  amixer -c 0 sset 'TDMIN_B SRC SEL' 'IN 1'
  amixer -c 0 sset 'TODDR_A SRC SEL' 'IN 1'

  # Workaround for ethernet PHY reset problem
  for m in dwmac_meson8b dwmac_generic stmmac_platform stmmac mdio_mux_meson_g12a mdio_mux pcs_xpcs mdio_devres of_mdio; do
    grep --quiet "^$m " /proc/modules || continue
    rmmod "$m"
  done
  sleep 0.5
  modprobe mdio_mux_meson_g12a
  modprobe dwmac_meson8b

  # Workaround for SDIO wifi not loading
  echo ffe03000.mmc >/sys/class/mmc_host/mmc2/device/driver/unbind
  echo ffe03000.mmc >/sys/bus/platform/drivers/meson-gx-mmc/bind
}

init_rk3588() {
  # shellcheck disable=SC2043
  for m in dwmac_rk; do
    grep --quiet "^$m " /proc/modules || continue
    rmmod "$m"
  done
  # manual ethernet phy reset
  gpioset -c 3 -t 100ms,100ms,0ms 23=1 \
    || echo "E: gpioset for phy reset failed (requires gpiod >= 2.0)" >&2
  sleep 0.5
  modprobe dwmac_rk
}

init_wm8960() {
  # Fix WM8960 being too quiet
  amixer -c 0 sset Playback 255
  amixer -c 0 sset 'Left Output Mixer PCM' on
  amixer -c 0 sset 'Right Output Mixer PCM' on
  # This switch in WM8960 needs to be on for the headset mic input to work
  amixer -c 0 sset 'Left Input Mixer Boost' on
}

init_tlv320aic3100() {
  # Set up speaker on MNT Pocket Reform
  amixer -c 0 sset 'Speaker Driver' on
  amixer -c 0 sset 'Output Left From Left DAC' on
  amixer -c 0 sset 'Output Right From Right DAC' on
  amixer -c 0 sset 'Speaker Analog' 127
  amixer -c 0 sset 'DAC' 127
  amixer -c 0 sset 'HP Analog' 127
  amixer -c 0 sset 'HP Driver' on
  amixer -c 0 sset 'HP Left' on
  amixer -c 0 sset 'HP Right' on
}

init_qca9377_wifi() {
  if test -e /proc/modules && grep --quiet '^qcacld2 ' /proc/modules; then
    echo "I: Module qcacld2 is already loaded" >&2
    return 0
  fi

  # load out-of-tree Wi-Fi driver
  KERNVER_EXACT=$(dpkg-query --show --showformat '${Version}' "linux-image-$(uname -r)")
  modprobe cfg80211

  # without "-f", insmod will fail with "Invalid module format" even though
  # the kernel version matches
  echo "I: Try to load exact match wifi driver: ${KERNVER_EXACT}" >&2
  if [ -f "/opt/reform-qcacld2/qcacld2-${KERNVER_EXACT}.ko" ]; then
    echo "I: Found exact match for kernel ${KERNVER_EXACT}" >&2
    if insmod -f "/opt/reform-qcacld2/qcacld2-${KERNVER_EXACT}.ko"; then
      echo "I: Successfully loaded exact match driver" >&2
      return 0
    else
      echo "W: Failed to load exact match driver. Try other available driver" >&2
    fi
  fi

  # Find other potential qcacld2 drivers
  potential_drivers=$(find /opt/reform-qcacld2/qcacld2-*.ko -maxdepth 1 -type f -print0 | sort -Vzr)

  # Try to load available drivers, starting with the highest version number, until one works
  for driver in $potential_drivers; do
    echo "I: Attempting to load wifi driver: $driver" >&2
    if insmod -f "$driver"; then
      echo "I: Successfully loaded driver: $driver" >&2
      return 0
    else
      echo "W: Failed to load driver: $driver, trying next" >&2
    fi
  done

  echo "E: Failed to load any compatible qcacld2 driver" >&2
  return 1

}
case "$(cat /proc/device-tree/model)" in
  "MNT Reform 2" | "MNT Reform 2 HDMI")
    # Workaround for WM8960 sometimes not coming out of reset
    if [ ! -e /sys/bus/i2c/drivers/wm8960/2-001a ]; then
      echo 2-001a >/sys/bus/i2c/drivers/wm8960/bind
    fi
    test -e /sys/bus/i2c/drivers/wm8960/2-001a

    init_wm8960
    ;;
  "MNT Reform 2 with BPI-CM4 Module")
    # Workaround for WM8960 sometimes not coming out of reset
    if [ ! -e /sys/bus/i2c/drivers/wm8960/1-001a ]; then
      echo 1-001a >/sys/bus/i2c/drivers/wm8960/bind
    fi
    test -e /sys/bus/i2c/drivers/wm8960/1-001a

    init_a311d
    init_wm8960
    ;;
  "MNT Reform 2 with LS1028A Module")
    # Workaround for a DWC3 USB Controller regression

    # Assert USB hub reset
    gpioset 2 13=0
    # Reload DWC3 module
    rmmod dwc3
    modprobe dwc3
    # Deassert USB hub reset
    gpioset 2 13=1

    # Select "ondemand" CPU frequency scaling governor
    echo ondemand >/sys/devices/system/cpu/cpufreq/policy0/scaling_governor

    # the functionality of the dwc3 module depends on the loglevel
    # loglevel=3 makes usb not work
    # loglevel=7 makes usb work most of the time
    # https://community.mnt.re/t/no-keyboard-input-with-ls1028-board-and-latest-image/1994/13
    dmesg -n 7
    ;;
  "MNT Pocket Reform with i.MX8MP Module")
    init_qca9377_wifi || echo "W: init_qca9377_wifi failed" >&2
    init_tlv320aic3100
    ;;
  "MNT Pocket Reform with BPI-CM4 Module")
    init_a311d
    init_tlv320aic3100
    ;;
  "MNT Reform 2 with i.MX8MP Module")
    init_wm8960
    init_qca9377_wifi || echo "W: init_qca9377_wifi failed" >&2
    ;;
  "MNT Reform 2 with RCORE RK3588 Module")
    init_rk3588
    init_wm8960
    ;;
  "MNT Pocket Reform with RCORE RK3588 Module")
    init_rk3588
    init_tlv320aic3100
    ;;
  "MNT Reform Next with RCORE RK3588 Module")
    init_rk3588
    init_tlv320aic3100
    ;;
  *)
    echo "E: Your platform $(cat /proc/device-tree/model) is not supported by reform-tools" >&2
    exit 1
    ;;
esac
